diff --git a/Cargo.lock b/Cargo.lock index 934f9af4..57930eec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1851,7 +1851,6 @@ dependencies = [ "miette", "multibase", "multihash", - "num-traits", "ouroboros", "p256", "rand 0.9.2", diff --git a/README.md b/README.md index 12efdf82..38694e71 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Jacquard is broken up into several crates for modularity. The correct one to use - `jacquard-oauth`: atproto OAuth implementation [![Crates.io](https://img.shields.io/crates/v/jacquard-oauth.svg)](https://crates.io/crates/jacquard-oauth) [![Documentation](https://docs.rs/jacquard-oauth/badge.svg)](https://docs.rs/jacquard-oauth) - `jacquard-identity`: Identity resolution [![Crates.io](https://img.shields.io/crates/v/jacquard-identity.svg)](https://crates.io/crates/jacquard-identity) [![Documentation](https://docs.rs/jacquard-identity/badge.svg)](https://docs.rs/jacquard-identity) - `jacquard-lexicon`: Lexicon parsing and code generation [![Crates.io](https://img.shields.io/crates/v/jacquard-lexicon.svg)](https://crates.io/crates/jacquard-lexicon) [![Documentation](https://docs.rs/jacquard-lexicon/badge.svg)](https://docs.rs/jacquard-lexicon) -- `jacquard-derive`: Derive macros for lexicon types [![Crates.io](https://img.shields.io/crates/v/jacquard-derive.svg)](https://crates.io/crates/jacquard-derive) [![Documentation](https://docs.rs/jacquard-derive/badge.svg)](https://docs.rs/jacquard-derive) +- `jacquard-derive`: Macros for lexicon types [![Crates.io](https://img.shields.io/crates/v/jacquard-derive.svg)](https://crates.io/crates/jacquard-derive) [![Documentation](https://docs.rs/jacquard-derive/badge.svg)](https://docs.rs/jacquard-derive) ## Changelog diff --git a/crates/jacquard-api/src/app_blebbit/authr/folder/record.rs b/crates/jacquard-api/src/app_blebbit/authr/folder/record.rs index e50d2346..cd44e25f 100644 --- a/crates/jacquard-api/src/app_blebbit/authr/folder/record.rs +++ b/crates/jacquard-api/src/app_blebbit/authr/folder/record.rs @@ -27,6 +27,44 @@ pub struct Record<'a> { pub public: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RecordGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Record<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RecordRecord; +impl jacquard_common::xrpc::XrpcResp for RecordRecord { + const NSID: &'static str = "app.blebbit.authr.folder.record"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RecordGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Record<'_> { const NSID: &'static str = "app.blebbit.authr.folder.record"; + type Record = RecordRecord; +} + +impl From> for Record<'static> { + fn from(output: RecordGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_blebbit/authr/group/record.rs b/crates/jacquard-api/src/app_blebbit/authr/group/record.rs index f5cccf44..6a5eb09b 100644 --- a/crates/jacquard-api/src/app_blebbit/authr/group/record.rs +++ b/crates/jacquard-api/src/app_blebbit/authr/group/record.rs @@ -33,6 +33,44 @@ pub struct Record<'a> { pub public: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RecordGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Record<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RecordRecord; +impl jacquard_common::xrpc::XrpcResp for RecordRecord { + const NSID: &'static str = "app.blebbit.authr.group.record"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RecordGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Record<'_> { const NSID: &'static str = "app.blebbit.authr.group.record"; + type Record = RecordRecord; +} + +impl From> for Record<'static> { + fn from(output: RecordGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_blebbit/authr/page/record.rs b/crates/jacquard-api/src/app_blebbit/authr/page/record.rs index 2bb85d20..fcf5f18f 100644 --- a/crates/jacquard-api/src/app_blebbit/authr/page/record.rs +++ b/crates/jacquard-api/src/app_blebbit/authr/page/record.rs @@ -30,6 +30,44 @@ pub struct Record<'a> { pub public: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RecordGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Record<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RecordRecord; +impl jacquard_common::xrpc::XrpcResp for RecordRecord { + const NSID: &'static str = "app.blebbit.authr.page.record"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RecordGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Record<'_> { const NSID: &'static str = "app.blebbit.authr.page.record"; + type Record = RecordRecord; +} + +impl From> for Record<'static> { + fn from(output: RecordGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/actor/profile.rs b/crates/jacquard-api/src/app_bsky/actor/profile.rs index cb1044b5..ef86c772 100644 --- a/crates/jacquard-api/src/app_bsky/actor/profile.rs +++ b/crates/jacquard-api/src/app_bsky/actor/profile.rs @@ -58,6 +58,44 @@ pub struct Profile<'a> { pub website: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "app.bsky.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "app.bsky.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/actor/status.rs b/crates/jacquard-api/src/app_bsky/actor/status.rs index 31bead2f..055f1fa0 100644 --- a/crates/jacquard-api/src/app_bsky/actor/status.rs +++ b/crates/jacquard-api/src/app_bsky/actor/status.rs @@ -49,6 +49,44 @@ pub struct Status<'a> { pub status: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatusGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Status<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatusRecord; +impl jacquard_common::xrpc::XrpcResp for StatusRecord { + const NSID: &'static str = "app.bsky.actor.status"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatusGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Status<'_> { const NSID: &'static str = "app.bsky.actor.status"; + type Record = StatusRecord; +} + +impl From> for Status<'static> { + fn from(output: StatusGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/feed/generator.rs b/crates/jacquard-api/src/app_bsky/feed/generator.rs index 7eebc3bd..65a40f4b 100644 --- a/crates/jacquard-api/src/app_bsky/feed/generator.rs +++ b/crates/jacquard-api/src/app_bsky/feed/generator.rs @@ -8,13 +8,7 @@ ///Record declaring of the existence of a feed generator, and containing metadata about it. The record can exist in any repository. #[jacquard_derive::lexicon] #[derive( - serde::Serialize, - serde::Deserialize, - Debug, - Clone, - PartialEq, - Eq, - jacquard_derive::IntoStatic + serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic, )] #[serde(rename_all = "camelCase")] pub struct Generator<'a> { @@ -33,9 +27,7 @@ pub struct Generator<'a> { pub description: std::option::Option>, #[serde(skip_serializing_if = "std::option::Option::is_none")] #[serde(borrow)] - pub description_facets: std::option::Option< - Vec>, - >, + pub description_facets: std::option::Option>>, #[serde(borrow)] pub did: jacquard_common::types::string::Did<'a>, #[serde(borrow)] @@ -46,6 +38,38 @@ pub struct Generator<'a> { pub labels: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic, +)] +#[serde(rename_all = "camelCase")] +pub struct GeneratorGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Generator<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GeneratorRecord; +impl jacquard_common::xrpc::XrpcResp for GeneratorRecord { + const NSID: &'static str = "app.bsky.feed.generator"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GeneratorGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Generator<'_> { const NSID: &'static str = "app.bsky.feed.generator"; -} \ No newline at end of file + type Record = GeneratorRecord; +} + +impl From> for Generator<'static> { + fn from(output: GeneratorGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } +} diff --git a/crates/jacquard-api/src/app_bsky/feed/like.rs b/crates/jacquard-api/src/app_bsky/feed/like.rs index f0468b92..b9f77e0e 100644 --- a/crates/jacquard-api/src/app_bsky/feed/like.rs +++ b/crates/jacquard-api/src/app_bsky/feed/like.rs @@ -26,6 +26,44 @@ pub struct Like<'a> { pub via: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LikeGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Like<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LikeRecord; +impl jacquard_common::xrpc::XrpcResp for LikeRecord { + const NSID: &'static str = "app.bsky.feed.like"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LikeGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Like<'_> { const NSID: &'static str = "app.bsky.feed.like"; + type Record = LikeRecord; +} + +impl From> for Like<'static> { + fn from(output: LikeGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/feed/post.rs b/crates/jacquard-api/src/app_bsky/feed/post.rs index 2248ae61..3b24de69 100644 --- a/crates/jacquard-api/src/app_bsky/feed/post.rs +++ b/crates/jacquard-api/src/app_bsky/feed/post.rs @@ -97,8 +97,46 @@ pub enum PostEmbed<'a> { RecordWithMedia(Box>), } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "app.bsky.feed.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "app.bsky.feed.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/app_bsky/feed/postgate.rs b/crates/jacquard-api/src/app_bsky/feed/postgate.rs index 5a1c480a..eb0805b0 100644 --- a/crates/jacquard-api/src/app_bsky/feed/postgate.rs +++ b/crates/jacquard-api/src/app_bsky/feed/postgate.rs @@ -49,6 +49,44 @@ pub struct Postgate<'a> { pub post: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostgateGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Postgate<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostgateRecord; +impl jacquard_common::xrpc::XrpcResp for PostgateRecord { + const NSID: &'static str = "app.bsky.feed.postgate"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostgateGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Postgate<'_> { const NSID: &'static str = "app.bsky.feed.postgate"; + type Record = PostgateRecord; +} + +impl From> for Postgate<'static> { + fn from(output: PostgateGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/feed/repost.rs b/crates/jacquard-api/src/app_bsky/feed/repost.rs index 08c2e572..acc5df75 100644 --- a/crates/jacquard-api/src/app_bsky/feed/repost.rs +++ b/crates/jacquard-api/src/app_bsky/feed/repost.rs @@ -26,6 +26,44 @@ pub struct Repost<'a> { pub via: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RepostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Repost<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RepostRecord; +impl jacquard_common::xrpc::XrpcResp for RepostRecord { + const NSID: &'static str = "app.bsky.feed.repost"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RepostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Repost<'_> { const NSID: &'static str = "app.bsky.feed.repost"; + type Record = RepostRecord; +} + +impl From> for Repost<'static> { + fn from(output: RepostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/feed/threadgate.rs b/crates/jacquard-api/src/app_bsky/feed/threadgate.rs index 0637754b..48711eab 100644 --- a/crates/jacquard-api/src/app_bsky/feed/threadgate.rs +++ b/crates/jacquard-api/src/app_bsky/feed/threadgate.rs @@ -100,8 +100,46 @@ pub enum ThreadgateAllowItem<'a> { ListRule(Box>), } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ThreadgateGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Threadgate<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ThreadgateRecord; +impl jacquard_common::xrpc::XrpcResp for ThreadgateRecord { + const NSID: &'static str = "app.bsky.feed.threadgate"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ThreadgateGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Threadgate<'_> { const NSID: &'static str = "app.bsky.feed.threadgate"; + type Record = ThreadgateRecord; +} + +impl From> for Threadgate<'static> { + fn from(output: ThreadgateGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Allow replies from actors mentioned in your post. diff --git a/crates/jacquard-api/src/app_bsky/graph/block.rs b/crates/jacquard-api/src/app_bsky/graph/block.rs index a14b3587..77764fa2 100644 --- a/crates/jacquard-api/src/app_bsky/graph/block.rs +++ b/crates/jacquard-api/src/app_bsky/graph/block.rs @@ -24,6 +24,44 @@ pub struct Block<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BlockGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Block<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BlockRecord; +impl jacquard_common::xrpc::XrpcResp for BlockRecord { + const NSID: &'static str = "app.bsky.graph.block"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BlockGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Block<'_> { const NSID: &'static str = "app.bsky.graph.block"; + type Record = BlockRecord; +} + +impl From> for Block<'static> { + fn from(output: BlockGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/follow.rs b/crates/jacquard-api/src/app_bsky/graph/follow.rs index 0cf44ba4..0b030d6d 100644 --- a/crates/jacquard-api/src/app_bsky/graph/follow.rs +++ b/crates/jacquard-api/src/app_bsky/graph/follow.rs @@ -23,6 +23,44 @@ pub struct Follow<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct FollowGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Follow<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct FollowRecord; +impl jacquard_common::xrpc::XrpcResp for FollowRecord { + const NSID: &'static str = "app.bsky.graph.follow"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = FollowGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Follow<'_> { const NSID: &'static str = "app.bsky.graph.follow"; + type Record = FollowRecord; +} + +impl From> for Follow<'static> { + fn from(output: FollowGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/list.rs b/crates/jacquard-api/src/app_bsky/graph/list.rs index cfd58268..a4511dd9 100644 --- a/crates/jacquard-api/src/app_bsky/graph/list.rs +++ b/crates/jacquard-api/src/app_bsky/graph/list.rs @@ -41,6 +41,44 @@ pub struct List<'a> { pub purpose: crate::app_bsky::graph::ListPurpose<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: List<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListRecord; +impl jacquard_common::xrpc::XrpcResp for ListRecord { + const NSID: &'static str = "app.bsky.graph.list"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for List<'_> { const NSID: &'static str = "app.bsky.graph.list"; + type Record = ListRecord; +} + +impl From> for List<'static> { + fn from(output: ListGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/listblock.rs b/crates/jacquard-api/src/app_bsky/graph/listblock.rs index 3eb31464..be9eb9fd 100644 --- a/crates/jacquard-api/src/app_bsky/graph/listblock.rs +++ b/crates/jacquard-api/src/app_bsky/graph/listblock.rs @@ -24,6 +24,44 @@ pub struct Listblock<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListblockGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Listblock<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListblockRecord; +impl jacquard_common::xrpc::XrpcResp for ListblockRecord { + const NSID: &'static str = "app.bsky.graph.listblock"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListblockGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Listblock<'_> { const NSID: &'static str = "app.bsky.graph.listblock"; + type Record = ListblockRecord; +} + +impl From> for Listblock<'static> { + fn from(output: ListblockGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/listitem.rs b/crates/jacquard-api/src/app_bsky/graph/listitem.rs index e830cd64..1116dbb7 100644 --- a/crates/jacquard-api/src/app_bsky/graph/listitem.rs +++ b/crates/jacquard-api/src/app_bsky/graph/listitem.rs @@ -27,6 +27,44 @@ pub struct Listitem<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListitemGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Listitem<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListitemRecord; +impl jacquard_common::xrpc::XrpcResp for ListitemRecord { + const NSID: &'static str = "app.bsky.graph.listitem"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListitemGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Listitem<'_> { const NSID: &'static str = "app.bsky.graph.listitem"; + type Record = ListitemRecord; +} + +impl From> for Listitem<'static> { + fn from(output: ListitemGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/starterpack.rs b/crates/jacquard-api/src/app_bsky/graph/starterpack.rs index c046eeb5..7c124f90 100644 --- a/crates/jacquard-api/src/app_bsky/graph/starterpack.rs +++ b/crates/jacquard-api/src/app_bsky/graph/starterpack.rs @@ -56,6 +56,44 @@ pub struct Starterpack<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StarterpackGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Starterpack<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StarterpackRecord; +impl jacquard_common::xrpc::XrpcResp for StarterpackRecord { + const NSID: &'static str = "app.bsky.graph.starterpack"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StarterpackGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Starterpack<'_> { const NSID: &'static str = "app.bsky.graph.starterpack"; + type Record = StarterpackRecord; +} + +impl From> for Starterpack<'static> { + fn from(output: StarterpackGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/graph/verification.rs b/crates/jacquard-api/src/app_bsky/graph/verification.rs index 03c8d679..b52d48e5 100644 --- a/crates/jacquard-api/src/app_bsky/graph/verification.rs +++ b/crates/jacquard-api/src/app_bsky/graph/verification.rs @@ -31,6 +31,44 @@ pub struct Verification<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct VerificationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Verification<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct VerificationRecord; +impl jacquard_common::xrpc::XrpcResp for VerificationRecord { + const NSID: &'static str = "app.bsky.graph.verification"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = VerificationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Verification<'_> { const NSID: &'static str = "app.bsky.graph.verification"; + type Record = VerificationRecord; +} + +impl From> for Verification<'static> { + fn from(output: VerificationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/labeler/service.rs b/crates/jacquard-api/src/app_bsky/labeler/service.rs index b8609a86..2d696694 100644 --- a/crates/jacquard-api/src/app_bsky/labeler/service.rs +++ b/crates/jacquard-api/src/app_bsky/labeler/service.rs @@ -44,6 +44,44 @@ pub struct Service<'a> { >, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ServiceGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Service<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ServiceRecord; +impl jacquard_common::xrpc::XrpcResp for ServiceRecord { + const NSID: &'static str = "app.bsky.labeler.service"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ServiceGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Service<'_> { const NSID: &'static str = "app.bsky.labeler.service"; + type Record = ServiceRecord; +} + +impl From> for Service<'static> { + fn from(output: ServiceGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_bsky/notification/declaration.rs b/crates/jacquard-api/src/app_bsky/notification/declaration.rs index ea4d4990..0e9dd0f1 100644 --- a/crates/jacquard-api/src/app_bsky/notification/declaration.rs +++ b/crates/jacquard-api/src/app_bsky/notification/declaration.rs @@ -23,6 +23,44 @@ pub struct Declaration<'a> { pub allow_subscriptions: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DeclarationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Declaration<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DeclarationRecord; +impl jacquard_common::xrpc::XrpcResp for DeclarationRecord { + const NSID: &'static str = "app.bsky.notification.declaration"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DeclarationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Declaration<'_> { const NSID: &'static str = "app.bsky.notification.declaration"; + type Record = DeclarationRecord; +} + +impl From> for Declaration<'static> { + fn from(output: DeclarationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_ocho/edu/verification.rs b/crates/jacquard-api/src/app_ocho/edu/verification.rs index 7aa0ee60..63a868f4 100644 --- a/crates/jacquard-api/src/app_ocho/edu/verification.rs +++ b/crates/jacquard-api/src/app_ocho/edu/verification.rs @@ -28,6 +28,44 @@ pub struct Verification<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct VerificationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Verification<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct VerificationRecord; +impl jacquard_common::xrpc::XrpcResp for VerificationRecord { + const NSID: &'static str = "app.ocho.edu.verification"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = VerificationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Verification<'_> { const NSID: &'static str = "app.ocho.edu.verification"; + type Record = VerificationRecord; +} + +impl From> for Verification<'static> { + fn from(output: VerificationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/app_ocho/plugin/service.rs b/crates/jacquard-api/src/app_ocho/plugin/service.rs index e1629075..5392038f 100644 --- a/crates/jacquard-api/src/app_ocho/plugin/service.rs +++ b/crates/jacquard-api/src/app_ocho/plugin/service.rs @@ -26,6 +26,44 @@ pub struct Service<'a> { pub permissions: Vec>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ServiceGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Service<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ServiceRecord; +impl jacquard_common::xrpc::XrpcResp for ServiceRecord { + const NSID: &'static str = "app.ocho.plugin.service"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ServiceGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Service<'_> { const NSID: &'static str = "app.ocho.plugin.service"; + type Record = ServiceRecord; +} + +impl From> for Service<'static> { + fn from(output: ServiceGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/beauty_cybernetic/trustcow/review.rs b/crates/jacquard-api/src/beauty_cybernetic/trustcow/review.rs index a9f6dbdd..75343268 100644 --- a/crates/jacquard-api/src/beauty_cybernetic/trustcow/review.rs +++ b/crates/jacquard-api/src/beauty_cybernetic/trustcow/review.rs @@ -39,6 +39,44 @@ pub struct Review<'a> { pub transaction: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ReviewGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Review<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ReviewRecord; +impl jacquard_common::xrpc::XrpcResp for ReviewRecord { + const NSID: &'static str = "beauty.cybernetic.trustcow.review"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ReviewGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Review<'_> { const NSID: &'static str = "beauty.cybernetic.trustcow.review"; + type Record = ReviewRecord; +} + +impl From> for Review<'static> { + fn from(output: ReviewGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/beauty_cybernetic/trustcow/transaction.rs b/crates/jacquard-api/src/beauty_cybernetic/trustcow/transaction.rs index 8165c631..4067b987 100644 --- a/crates/jacquard-api/src/beauty_cybernetic/trustcow/transaction.rs +++ b/crates/jacquard-api/src/beauty_cybernetic/trustcow/transaction.rs @@ -43,6 +43,44 @@ pub struct Transaction<'a> { pub transaction_id: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct TransactionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Transaction<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct TransactionRecord; +impl jacquard_common::xrpc::XrpcResp for TransactionRecord { + const NSID: &'static str = "beauty.cybernetic.trustcow.transaction"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = TransactionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Transaction<'_> { const NSID: &'static str = "beauty.cybernetic.trustcow.transaction"; + type Record = TransactionRecord; +} + +impl From> for Transaction<'static> { + fn from(output: TransactionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/beauty_cybernetic/trustcow/warrant.rs b/crates/jacquard-api/src/beauty_cybernetic/trustcow/warrant.rs index 697aa2a3..0de1bd80 100644 --- a/crates/jacquard-api/src/beauty_cybernetic/trustcow/warrant.rs +++ b/crates/jacquard-api/src/beauty_cybernetic/trustcow/warrant.rs @@ -40,6 +40,44 @@ pub struct Warrant<'a> { pub warrant_type: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct WarrantGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Warrant<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct WarrantRecord; +impl jacquard_common::xrpc::XrpcResp for WarrantRecord { + const NSID: &'static str = "beauty.cybernetic.trustcow.warrant"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = WarrantGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Warrant<'_> { const NSID: &'static str = "beauty.cybernetic.trustcow.warrant"; + type Record = WarrantRecord; +} + +impl From> for Warrant<'static> { + fn from(output: WarrantGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blog_pckt/blog.rs b/crates/jacquard-api/src/blog_pckt/blog.rs index 6e085e86..f002b307 100644 --- a/crates/jacquard-api/src/blog_pckt/blog.rs +++ b/crates/jacquard-api/src/blog_pckt/blog.rs @@ -38,8 +38,46 @@ pub struct Blog<'a> { pub url: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BlogGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Blog<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BlogRecord; +impl jacquard_common::xrpc::XrpcResp for BlogRecord { + const NSID: &'static str = "blog.pckt.blog"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BlogGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Blog<'_> { const NSID: &'static str = "blog.pckt.blog"; + type Record = BlogRecord; +} + +impl From> for Blog<'static> { + fn from(output: BlogGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/blog_pckt/post.rs b/crates/jacquard-api/src/blog_pckt/post.rs index 55e368eb..43a1d282 100644 --- a/crates/jacquard-api/src/blog_pckt/post.rs +++ b/crates/jacquard-api/src/blog_pckt/post.rs @@ -43,6 +43,44 @@ pub struct Post<'a> { pub url: jacquard_common::types::string::Uri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "blog.pckt.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "blog.pckt.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blog_pckt/publication.rs b/crates/jacquard-api/src/blog_pckt/publication.rs index e76b33e3..13549983 100644 --- a/crates/jacquard-api/src/blog_pckt/publication.rs +++ b/crates/jacquard-api/src/blog_pckt/publication.rs @@ -48,6 +48,44 @@ pub struct Publication<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PublicationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Publication<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PublicationRecord; +impl jacquard_common::xrpc::XrpcResp for PublicationRecord { + const NSID: &'static str = "blog.pckt.publication"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PublicationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Publication<'_> { const NSID: &'static str = "blog.pckt.publication"; + type Record = PublicationRecord; +} + +impl From> for Publication<'static> { + fn from(output: PublicationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blog_pckt/theme.rs b/crates/jacquard-api/src/blog_pckt/theme.rs index 97221ad6..3a006953 100644 --- a/crates/jacquard-api/src/blog_pckt/theme.rs +++ b/crates/jacquard-api/src/blog_pckt/theme.rs @@ -30,8 +30,46 @@ pub struct Theme<'a> { pub light: crate::blog_pckt::theme::Palette<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ThemeGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Theme<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ThemeRecord; +impl jacquard_common::xrpc::XrpcResp for ThemeRecord { + const NSID: &'static str = "blog.pckt.theme"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ThemeGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Theme<'_> { const NSID: &'static str = "blog.pckt.theme"; + type Record = ThemeRecord; +} + +impl From> for Theme<'static> { + fn from(output: ThemeGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Color palette with CSS hex values diff --git a/crates/jacquard-api/src/blue__2048/game.rs b/crates/jacquard-api/src/blue__2048/game.rs index f81abb36..861a89c0 100644 --- a/crates/jacquard-api/src/blue__2048/game.rs +++ b/crates/jacquard-api/src/blue__2048/game.rs @@ -33,6 +33,44 @@ pub struct Game<'a> { pub won: bool, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GameGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Game<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GameRecord; +impl jacquard_common::xrpc::XrpcResp for GameRecord { + const NSID: &'static str = "blue.2048.game"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GameGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Game<'_> { const NSID: &'static str = "blue.2048.game"; + type Record = GameRecord; +} + +impl From> for Game<'static> { + fn from(output: GameGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/key/game.rs b/crates/jacquard-api/src/blue__2048/key/game.rs index e84c386b..281d42b7 100644 --- a/crates/jacquard-api/src/blue__2048/key/game.rs +++ b/crates/jacquard-api/src/blue__2048/key/game.rs @@ -24,6 +24,44 @@ pub struct Game<'a> { pub key: crate::blue__2048::key::Key<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GameGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Game<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GameRecord; +impl jacquard_common::xrpc::XrpcResp for GameRecord { + const NSID: &'static str = "blue.2048.key.game"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GameGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Game<'_> { const NSID: &'static str = "blue.2048.key.game"; + type Record = GameRecord; +} + +impl From> for Game<'static> { + fn from(output: GameGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/key/player/stats.rs b/crates/jacquard-api/src/blue__2048/key/player/stats.rs index 39b91c07..7004f9b5 100644 --- a/crates/jacquard-api/src/blue__2048/key/player/stats.rs +++ b/crates/jacquard-api/src/blue__2048/key/player/stats.rs @@ -24,6 +24,44 @@ pub struct Stats<'a> { pub key: crate::blue__2048::key::Key<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Stats<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatsRecord; +impl jacquard_common::xrpc::XrpcResp for StatsRecord { + const NSID: &'static str = "blue.2048.key.player.stats"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Stats<'_> { const NSID: &'static str = "blue.2048.key.player.stats"; + type Record = StatsRecord; +} + +impl From> for Stats<'static> { + fn from(output: StatsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/player/profile.rs b/crates/jacquard-api/src/blue__2048/player/profile.rs index 92a8dd62..30194c27 100644 --- a/crates/jacquard-api/src/blue__2048/player/profile.rs +++ b/crates/jacquard-api/src/blue__2048/player/profile.rs @@ -26,6 +26,44 @@ pub struct Profile<'a> { pub sync_status: crate::blue__2048::SyncStatus<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "blue.2048.player.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "blue.2048.player.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/player/stats.rs b/crates/jacquard-api/src/blue__2048/player/stats.rs index df65693f..a083f5de 100644 --- a/crates/jacquard-api/src/blue__2048/player/stats.rs +++ b/crates/jacquard-api/src/blue__2048/player/stats.rs @@ -38,6 +38,44 @@ pub struct Stats<'a> { pub total_score: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Stats<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatsRecord; +impl jacquard_common::xrpc::XrpcResp for StatsRecord { + const NSID: &'static str = "blue.2048.player.stats"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Stats<'_> { const NSID: &'static str = "blue.2048.player.stats"; + type Record = StatsRecord; +} + +impl From> for Stats<'static> { + fn from(output: StatsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/verification/game.rs b/crates/jacquard-api/src/blue__2048/verification/game.rs index db139739..864585fa 100644 --- a/crates/jacquard-api/src/blue__2048/verification/game.rs +++ b/crates/jacquard-api/src/blue__2048/verification/game.rs @@ -28,6 +28,44 @@ pub struct Game<'a> { >, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GameGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Game<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GameRecord; +impl jacquard_common::xrpc::XrpcResp for GameRecord { + const NSID: &'static str = "blue.2048.verification.game"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GameGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Game<'_> { const NSID: &'static str = "blue.2048.verification.game"; + type Record = GameRecord; +} + +impl From> for Game<'static> { + fn from(output: GameGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue__2048/verification/stats.rs b/crates/jacquard-api/src/blue__2048/verification/stats.rs index 8f65b915..89c74eff 100644 --- a/crates/jacquard-api/src/blue__2048/verification/stats.rs +++ b/crates/jacquard-api/src/blue__2048/verification/stats.rs @@ -28,6 +28,44 @@ pub struct Stats<'a> { >, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Stats<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatsRecord; +impl jacquard_common::xrpc::XrpcResp for StatsRecord { + const NSID: &'static str = "blue.2048.verification.stats"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Stats<'_> { const NSID: &'static str = "blue.2048.verification.stats"; + type Record = StatsRecord; +} + +impl From> for Stats<'static> { + fn from(output: StatsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue_atplane/fav_client.rs b/crates/jacquard-api/src/blue_atplane/fav_client.rs index 093b8fa2..e32ea300 100644 --- a/crates/jacquard-api/src/blue_atplane/fav_client.rs +++ b/crates/jacquard-api/src/blue_atplane/fav_client.rs @@ -23,6 +23,44 @@ pub struct FavClient<'a> { pub fav_client: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct FavClientGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: FavClient<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct FavClientRecord; +impl jacquard_common::xrpc::XrpcResp for FavClientRecord { + const NSID: &'static str = "blue.atplane.favClient"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = FavClientGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for FavClient<'_> { const NSID: &'static str = "blue.atplane.favClient"; + type Record = FavClientRecord; +} + +impl From> for FavClient<'static> { + fn from(output: FavClientGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue_linkat/board.rs b/crates/jacquard-api/src/blue_linkat/board.rs index ac277836..eb3f22f4 100644 --- a/crates/jacquard-api/src/blue_linkat/board.rs +++ b/crates/jacquard-api/src/blue_linkat/board.rs @@ -49,6 +49,44 @@ pub struct Board<'a> { pub cards: Vec>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BoardGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Board<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BoardRecord; +impl jacquard_common::xrpc::XrpcResp for BoardRecord { + const NSID: &'static str = "blue.linkat.board"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BoardGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Board<'_> { const NSID: &'static str = "blue.linkat.board"; + type Record = BoardRecord; +} + +impl From> for Board<'static> { + fn from(output: BoardGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/blue_zio/atfile/lock.rs b/crates/jacquard-api/src/blue_zio/atfile/lock.rs index ee59efac..e212930e 100644 --- a/crates/jacquard-api/src/blue_zio/atfile/lock.rs +++ b/crates/jacquard-api/src/blue_zio/atfile/lock.rs @@ -22,6 +22,44 @@ pub struct Lock<'a> { pub lock: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LockGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Lock<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LockRecord; +impl jacquard_common::xrpc::XrpcResp for LockRecord { + const NSID: &'static str = "blue.zio.atfile.lock"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LockGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Lock<'_> { const NSID: &'static str = "blue.zio.atfile.lock"; + type Record = LockRecord; +} + +impl From> for Lock<'static> { + fn from(output: LockGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/buzz_bookhive/book.rs b/crates/jacquard-api/src/buzz_bookhive/book.rs index 92961da2..7e1b8dde 100644 --- a/crates/jacquard-api/src/buzz_bookhive/book.rs +++ b/crates/jacquard-api/src/buzz_bookhive/book.rs @@ -50,6 +50,44 @@ pub struct Book<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BookGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Book<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BookRecord; +impl jacquard_common::xrpc::XrpcResp for BookRecord { + const NSID: &'static str = "buzz.bookhive.book"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BookGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Book<'_> { const NSID: &'static str = "buzz.bookhive.book"; + type Record = BookRecord; +} + +impl From> for Book<'static> { + fn from(output: BookGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/buzz_bookhive/buzz.rs b/crates/jacquard-api/src/buzz_bookhive/buzz.rs index 469c3c05..a2646a3d 100644 --- a/crates/jacquard-api/src/buzz_bookhive/buzz.rs +++ b/crates/jacquard-api/src/buzz_bookhive/buzz.rs @@ -29,6 +29,44 @@ pub struct Buzz<'a> { pub parent: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BuzzGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Buzz<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BuzzRecord; +impl jacquard_common::xrpc::XrpcResp for BuzzRecord { + const NSID: &'static str = "buzz.bookhive.buzz"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BuzzGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Buzz<'_> { const NSID: &'static str = "buzz.bookhive.buzz"; + type Record = BuzzRecord; +} + +impl From> for Buzz<'static> { + fn from(output: BuzzGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/buzz_bookhive/hive_book.rs b/crates/jacquard-api/src/buzz_bookhive/hive_book.rs index 448ff651..4fb7e698 100644 --- a/crates/jacquard-api/src/buzz_bookhive/hive_book.rs +++ b/crates/jacquard-api/src/buzz_bookhive/hive_book.rs @@ -60,6 +60,44 @@ pub struct HiveBook<'a> { pub updated_at: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct HiveBookGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: HiveBook<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct HiveBookRecord; +impl jacquard_common::xrpc::XrpcResp for HiveBookRecord { + const NSID: &'static str = "buzz.bookhive.hiveBook"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = HiveBookGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for HiveBook<'_> { const NSID: &'static str = "buzz.bookhive.hiveBook"; + type Record = HiveBookRecord; +} + +impl From> for HiveBook<'static> { + fn from(output: HiveBookGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/chat_bsky/actor/declaration.rs b/crates/jacquard-api/src/chat_bsky/actor/declaration.rs index 5d5b90bf..3d1356f3 100644 --- a/crates/jacquard-api/src/chat_bsky/actor/declaration.rs +++ b/crates/jacquard-api/src/chat_bsky/actor/declaration.rs @@ -22,6 +22,44 @@ pub struct Declaration<'a> { pub allow_incoming: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DeclarationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Declaration<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DeclarationRecord; +impl jacquard_common::xrpc::XrpcResp for DeclarationRecord { + const NSID: &'static str = "chat.bsky.actor.declaration"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DeclarationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Declaration<'_> { const NSID: &'static str = "chat.bsky.actor.declaration"; + type Record = DeclarationRecord; +} + +impl From> for Declaration<'static> { + fn from(output: DeclarationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/com_atproto/lexicon/schema.rs b/crates/jacquard-api/src/com_atproto/lexicon/schema.rs index bc1e866e..67f1fcd8 100644 --- a/crates/jacquard-api/src/com_atproto/lexicon/schema.rs +++ b/crates/jacquard-api/src/com_atproto/lexicon/schema.rs @@ -22,6 +22,44 @@ pub struct Schema<'a> { pub lexicon: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SchemaGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Schema<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SchemaRecord; +impl jacquard_common::xrpc::XrpcResp for SchemaRecord { + const NSID: &'static str = "com.atproto.lexicon.schema"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SchemaGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Schema<'_> { const NSID: &'static str = "com.atproto.lexicon.schema"; + type Record = SchemaRecord; +} + +impl From> for Schema<'static> { + fn from(output: SchemaGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/com_crabdance/nandi/post.rs b/crates/jacquard-api/src/com_crabdance/nandi/post.rs index 1f68b4c6..56d0cf4c 100644 --- a/crates/jacquard-api/src/com_crabdance/nandi/post.rs +++ b/crates/jacquard-api/src/com_crabdance/nandi/post.rs @@ -39,6 +39,44 @@ pub struct Post<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "com.crabdance.nandi.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "com.crabdance.nandi.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/com_shinolabs/pinksea/oekaki.rs b/crates/jacquard-api/src/com_shinolabs/pinksea/oekaki.rs index bbae931b..fce10fec 100644 --- a/crates/jacquard-api/src/com_shinolabs/pinksea/oekaki.rs +++ b/crates/jacquard-api/src/com_shinolabs/pinksea/oekaki.rs @@ -75,6 +75,44 @@ pub struct Oekaki<'a> { pub tags: std::option::Option>>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct OekakiGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Oekaki<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct OekakiRecord; +impl jacquard_common::xrpc::XrpcResp for OekakiRecord { + const NSID: &'static str = "com.shinolabs.pinksea.oekaki"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = OekakiGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Oekaki<'_> { const NSID: &'static str = "com.shinolabs.pinksea.oekaki"; + type Record = OekakiRecord; +} + +impl From> for Oekaki<'static> { + fn from(output: OekakiGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/com_shinolabs/pinksea/profile.rs b/crates/jacquard-api/src/com_shinolabs/pinksea/profile.rs index b1d4bf44..392a98e2 100644 --- a/crates/jacquard-api/src/com_shinolabs/pinksea/profile.rs +++ b/crates/jacquard-api/src/com_shinolabs/pinksea/profile.rs @@ -38,8 +38,46 @@ pub struct Profile<'a> { pub nickname: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "com.shinolabs.pinksea.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "com.shinolabs.pinksea.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/com_whtwnd/blog/entry.rs b/crates/jacquard-api/src/com_whtwnd/blog/entry.rs index 8faa291d..64d46c87 100644 --- a/crates/jacquard-api/src/com_whtwnd/blog/entry.rs +++ b/crates/jacquard-api/src/com_whtwnd/blog/entry.rs @@ -46,6 +46,44 @@ pub struct Entry<'a> { pub visibility: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct EntryGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Entry<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct EntryRecord; +impl jacquard_common::xrpc::XrpcResp for EntryRecord { + const NSID: &'static str = "com.whtwnd.blog.entry"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = EntryGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Entry<'_> { const NSID: &'static str = "com.whtwnd.blog.entry"; + type Record = EntryRecord; +} + +impl From> for Entry<'static> { + fn from(output: EntryGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/community_lexicon/bookmarks/bookmark.rs b/crates/jacquard-api/src/community_lexicon/bookmarks/bookmark.rs index 08743c9b..7c992fd9 100644 --- a/crates/jacquard-api/src/community_lexicon/bookmarks/bookmark.rs +++ b/crates/jacquard-api/src/community_lexicon/bookmarks/bookmark.rs @@ -27,6 +27,44 @@ pub struct Bookmark<'a> { pub tags: std::option::Option>>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BookmarkGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Bookmark<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BookmarkRecord; +impl jacquard_common::xrpc::XrpcResp for BookmarkRecord { + const NSID: &'static str = "community.lexicon.bookmarks.bookmark"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BookmarkGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Bookmark<'_> { const NSID: &'static str = "community.lexicon.bookmarks.bookmark"; + type Record = BookmarkRecord; +} + +impl From> for Bookmark<'static> { + fn from(output: BookmarkGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/community_lexicon/calendar/event.rs b/crates/jacquard-api/src/community_lexicon/calendar/event.rs index ef219feb..c56741cd 100644 --- a/crates/jacquard-api/src/community_lexicon/calendar/event.rs +++ b/crates/jacquard-api/src/community_lexicon/calendar/event.rs @@ -134,8 +134,46 @@ pub enum EventLocationsItem<'a> { Hthree(Box>), } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct EventGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Event<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct EventRecord; +impl jacquard_common::xrpc::XrpcResp for EventRecord { + const NSID: &'static str = "community.lexicon.calendar.event"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = EventGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Event<'_> { const NSID: &'static str = "community.lexicon.calendar.event"; + type Record = EventRecord; +} + +impl From> for Event<'static> { + fn from(output: EventGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///The mode of the event. diff --git a/crates/jacquard-api/src/community_lexicon/calendar/rsvp.rs b/crates/jacquard-api/src/community_lexicon/calendar/rsvp.rs index 13afc40f..7db6a9c3 100644 --- a/crates/jacquard-api/src/community_lexicon/calendar/rsvp.rs +++ b/crates/jacquard-api/src/community_lexicon/calendar/rsvp.rs @@ -60,8 +60,46 @@ pub struct Rsvp<'a> { pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RsvpGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Rsvp<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RsvpRecord; +impl jacquard_common::xrpc::XrpcResp for RsvpRecord { + const NSID: &'static str = "community.lexicon.calendar.rsvp"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RsvpGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Rsvp<'_> { const NSID: &'static str = "community.lexicon.calendar.rsvp"; + type Record = RsvpRecord; +} + +impl From> for Rsvp<'static> { + fn from(output: RsvpGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Not going to the event diff --git a/crates/jacquard-api/src/community_lexicon/interaction/like.rs b/crates/jacquard-api/src/community_lexicon/interaction/like.rs index 86493c8f..b8d56ee3 100644 --- a/crates/jacquard-api/src/community_lexicon/interaction/like.rs +++ b/crates/jacquard-api/src/community_lexicon/interaction/like.rs @@ -23,6 +23,44 @@ pub struct Like<'a> { pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LikeGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Like<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LikeRecord; +impl jacquard_common::xrpc::XrpcResp for LikeRecord { + const NSID: &'static str = "community.lexicon.interaction.like"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LikeGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Like<'_> { const NSID: &'static str = "community.lexicon.interaction.like"; + type Record = LikeRecord; +} + +impl From> for Like<'static> { + fn from(output: LikeGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/community_lexicon/payments/web_monetization.rs b/crates/jacquard-api/src/community_lexicon/payments/web_monetization.rs index e87d25a0..ba073a32 100644 --- a/crates/jacquard-api/src/community_lexicon/payments/web_monetization.rs +++ b/crates/jacquard-api/src/community_lexicon/payments/web_monetization.rs @@ -27,6 +27,44 @@ pub struct WebMonetization<'a> { pub note: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct WebMonetizationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: WebMonetization<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct WebMonetizationRecord; +impl jacquard_common::xrpc::XrpcResp for WebMonetizationRecord { + const NSID: &'static str = "community.lexicon.payments.webMonetization"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = WebMonetizationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for WebMonetization<'_> { const NSID: &'static str = "community.lexicon.payments.webMonetization"; + type Record = WebMonetizationRecord; +} + +impl From> for WebMonetization<'static> { + fn from(output: WebMonetizationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_baileytownsend/health/calories.rs b/crates/jacquard-api/src/dev_baileytownsend/health/calories.rs index 17e7c366..8e9ecd00 100644 --- a/crates/jacquard-api/src/dev_baileytownsend/health/calories.rs +++ b/crates/jacquard-api/src/dev_baileytownsend/health/calories.rs @@ -23,6 +23,44 @@ pub struct Calories<'a> { pub intake: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CaloriesGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Calories<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CaloriesRecord; +impl jacquard_common::xrpc::XrpcResp for CaloriesRecord { + const NSID: &'static str = "dev.baileytownsend.health.calories"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CaloriesGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Calories<'_> { const NSID: &'static str = "dev.baileytownsend.health.calories"; + type Record = CaloriesRecord; +} + +impl From> for Calories<'static> { + fn from(output: CaloriesGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_baileytownsend/health/rings.rs b/crates/jacquard-api/src/dev_baileytownsend/health/rings.rs index cb0a2736..9694c8f2 100644 --- a/crates/jacquard-api/src/dev_baileytownsend/health/rings.rs +++ b/crates/jacquard-api/src/dev_baileytownsend/health/rings.rs @@ -33,6 +33,44 @@ pub struct Rings<'a> { pub stand_hours: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RingsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Rings<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RingsRecord; +impl jacquard_common::xrpc::XrpcResp for RingsRecord { + const NSID: &'static str = "dev.baileytownsend.health.rings"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RingsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Rings<'_> { const NSID: &'static str = "dev.baileytownsend.health.rings"; + type Record = RingsRecord; +} + +impl From> for Rings<'static> { + fn from(output: RingsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_baileytownsend/health/steps.rs b/crates/jacquard-api/src/dev_baileytownsend/health/steps.rs index 1419885b..281b28a2 100644 --- a/crates/jacquard-api/src/dev_baileytownsend/health/steps.rs +++ b/crates/jacquard-api/src/dev_baileytownsend/health/steps.rs @@ -21,6 +21,44 @@ pub struct Steps<'a> { pub steps: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StepsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Steps<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StepsRecord; +impl jacquard_common::xrpc::XrpcResp for StepsRecord { + const NSID: &'static str = "dev.baileytownsend.health.steps"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StepsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Steps<'_> { const NSID: &'static str = "dev.baileytownsend.health.steps"; + type Record = StepsRecord; +} + +impl From> for Steps<'static> { + fn from(output: StepsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_baileytownsend/health/workout.rs b/crates/jacquard-api/src/dev_baileytownsend/health/workout.rs index 776eecb1..a41119d2 100644 --- a/crates/jacquard-api/src/dev_baileytownsend/health/workout.rs +++ b/crates/jacquard-api/src/dev_baileytownsend/health/workout.rs @@ -34,6 +34,44 @@ pub struct Workout<'a> { pub start_time: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct WorkoutGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Workout<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct WorkoutRecord; +impl jacquard_common::xrpc::XrpcResp for WorkoutRecord { + const NSID: &'static str = "dev.baileytownsend.health.workout"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = WorkoutGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Workout<'_> { const NSID: &'static str = "dev.baileytownsend.health.workout"; + type Record = WorkoutRecord; +} + +impl From> for Workout<'static> { + fn from(output: WorkoutGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/post.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/post.rs index 72763492..210b9306 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/post.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/post.rs @@ -36,6 +36,44 @@ pub struct Post<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.feed.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.feed.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/reply.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/reply.rs index 6a328b52..ea6b6911 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/reply.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/feed/reply.rs @@ -27,6 +27,44 @@ pub struct Reply<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ReplyGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Reply<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ReplyRecord; +impl jacquard_common::xrpc::XrpcResp for ReplyRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.feed.reply"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ReplyGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Reply<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.feed.reply"; + type Record = ReplyRecord; +} + +impl From> for Reply<'static> { + fn from(output: ReplyGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/announcement.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/announcement.rs index 5138e620..72c2caad 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/announcement.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/announcement.rs @@ -26,6 +26,44 @@ pub struct Announcement<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct AnnouncementGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Announcement<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct AnnouncementRecord; +impl jacquard_common::xrpc::XrpcResp for AnnouncementRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.announcement"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = AnnouncementGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Announcement<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.announcement"; + type Record = AnnouncementRecord; +} + +impl From> for Announcement<'static> { + fn from(output: AnnouncementGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/category.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/category.rs index 9893c1dd..e5c540f8 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/category.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/category.rs @@ -29,6 +29,44 @@ pub struct Category<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CategoryGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Category<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CategoryRecord; +impl jacquard_common::xrpc::XrpcResp for CategoryRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.category"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CategoryGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Category<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.category"; + type Record = CategoryRecord; +} + +impl From> for Category<'static> { + fn from(output: CategoryGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/group.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/group.rs index b73906f6..9c8db200 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/group.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/group.rs @@ -25,6 +25,44 @@ pub struct Group<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GroupGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Group<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GroupRecord; +impl jacquard_common::xrpc::XrpcResp for GroupRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.group"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GroupGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Group<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.group"; + type Record = GroupRecord; +} + +impl From> for Group<'static> { + fn from(output: GroupGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/identity.rs b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/identity.rs index ae1d2441..2fb6f263 100644 --- a/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/identity.rs +++ b/crates/jacquard-api/src/dev_fudgeu/experimental/atforumv1/forum/identity.rs @@ -28,6 +28,44 @@ pub struct Identity<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct IdentityGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Identity<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct IdentityRecord; +impl jacquard_common::xrpc::XrpcResp for IdentityRecord { + const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.identity"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = IdentityGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Identity<'_> { const NSID: &'static str = "dev.fudgeu.experimental.atforumv1.forum.identity"; + type Record = IdentityRecord; +} + +impl From> for Identity<'static> { + fn from(output: IdentityGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_ocbwoy3/blueboard/board.rs b/crates/jacquard-api/src/dev_ocbwoy3/blueboard/board.rs index b4d256aa..f83585f6 100644 --- a/crates/jacquard-api/src/dev_ocbwoy3/blueboard/board.rs +++ b/crates/jacquard-api/src/dev_ocbwoy3/blueboard/board.rs @@ -29,6 +29,44 @@ pub struct Board<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BoardGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Board<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BoardRecord; +impl jacquard_common::xrpc::XrpcResp for BoardRecord { + const NSID: &'static str = "dev.ocbwoy3.blueboard.board"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BoardGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Board<'_> { const NSID: &'static str = "dev.ocbwoy3.blueboard.board"; + type Record = BoardRecord; +} + +impl From> for Board<'static> { + fn from(output: BoardGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_ocbwoy3/blueboard/post.rs b/crates/jacquard-api/src/dev_ocbwoy3/blueboard/post.rs index 049a21d2..8058ae3e 100644 --- a/crates/jacquard-api/src/dev_ocbwoy3/blueboard/post.rs +++ b/crates/jacquard-api/src/dev_ocbwoy3/blueboard/post.rs @@ -31,6 +31,44 @@ pub struct Post<'a> { pub text: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "dev.ocbwoy3.blueboard.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "dev.ocbwoy3.blueboard.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/dev_regnault/webfishing/savefile.rs b/crates/jacquard-api/src/dev_regnault/webfishing/savefile.rs index 0396db0b..5ae7241e 100644 --- a/crates/jacquard-api/src/dev_regnault/webfishing/savefile.rs +++ b/crates/jacquard-api/src/dev_regnault/webfishing/savefile.rs @@ -24,6 +24,44 @@ pub struct Savefile<'a> { pub uri: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SavefileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Savefile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SavefileRecord; +impl jacquard_common::xrpc::XrpcResp for SavefileRecord { + const NSID: &'static str = "dev.regnault.webfishing.savefile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SavefileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Savefile<'_> { const NSID: &'static str = "dev.regnault.webfishing.savefile"; + type Record = SavefileRecord; +} + +impl From> for Savefile<'static> { + fn from(output: SavefileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/fyi_unravel/frontpage/comment.rs b/crates/jacquard-api/src/fyi_unravel/frontpage/comment.rs index 1f668214..91b7f69f 100644 --- a/crates/jacquard-api/src/fyi_unravel/frontpage/comment.rs +++ b/crates/jacquard-api/src/fyi_unravel/frontpage/comment.rs @@ -30,6 +30,44 @@ pub struct Comment<'a> { pub post: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CommentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Comment<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CommentRecord; +impl jacquard_common::xrpc::XrpcResp for CommentRecord { + const NSID: &'static str = "fyi.unravel.frontpage.comment"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CommentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Comment<'_> { const NSID: &'static str = "fyi.unravel.frontpage.comment"; + type Record = CommentRecord; +} + +impl From> for Comment<'static> { + fn from(output: CommentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/fyi_unravel/frontpage/post.rs b/crates/jacquard-api/src/fyi_unravel/frontpage/post.rs index fa0cdacf..a65ab42b 100644 --- a/crates/jacquard-api/src/fyi_unravel/frontpage/post.rs +++ b/crates/jacquard-api/src/fyi_unravel/frontpage/post.rs @@ -28,6 +28,44 @@ pub struct Post<'a> { pub url: jacquard_common::types::string::Uri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "fyi.unravel.frontpage.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "fyi.unravel.frontpage.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/fyi_unravel/frontpage/vote.rs b/crates/jacquard-api/src/fyi_unravel/frontpage/vote.rs index a71bd880..8163912f 100644 --- a/crates/jacquard-api/src/fyi_unravel/frontpage/vote.rs +++ b/crates/jacquard-api/src/fyi_unravel/frontpage/vote.rs @@ -24,6 +24,44 @@ pub struct Vote<'a> { pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct VoteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Vote<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct VoteRecord; +impl jacquard_common::xrpc::XrpcResp for VoteRecord { + const NSID: &'static str = "fyi.unravel.frontpage.vote"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = VoteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Vote<'_> { const NSID: &'static str = "fyi.unravel.frontpage.vote"; + type Record = VoteRecord; +} + +impl From> for Vote<'static> { + fn from(output: VoteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/moe_karashiiro/kpaste/paste.rs b/crates/jacquard-api/src/moe_karashiiro/kpaste/paste.rs index 5d9d2ce9..a6495385 100644 --- a/crates/jacquard-api/src/moe_karashiiro/kpaste/paste.rs +++ b/crates/jacquard-api/src/moe_karashiiro/kpaste/paste.rs @@ -35,6 +35,44 @@ pub struct Paste<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PasteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Paste<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PasteRecord; +impl jacquard_common::xrpc::XrpcResp for PasteRecord { + const NSID: &'static str = "moe.karashiiro.kpaste.paste"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PasteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Paste<'_> { const NSID: &'static str = "moe.karashiiro.kpaste.paste"; + type Record = PasteRecord; +} + +impl From> for Paste<'static> { + fn from(output: PasteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/my_skylights/rel.rs b/crates/jacquard-api/src/my_skylights/rel.rs index 244b2a21..336c9cd1 100644 --- a/crates/jacquard-api/src/my_skylights/rel.rs +++ b/crates/jacquard-api/src/my_skylights/rel.rs @@ -29,8 +29,46 @@ pub struct Rel<'a> { pub rating: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RelGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Rel<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RelRecord; +impl jacquard_common::xrpc::XrpcResp for RelRecord { + const NSID: &'static str = "my.skylights.rel"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RelGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Rel<'_> { const NSID: &'static str = "my.skylights.rel"; + type Record = RelRecord; +} + +impl From> for Rel<'static> { + fn from(output: RelGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/net_aftertheinter/coolthingtwo.rs b/crates/jacquard-api/src/net_aftertheinter/coolthingtwo.rs index de183de4..d57394e7 100644 --- a/crates/jacquard-api/src/net_aftertheinter/coolthingtwo.rs +++ b/crates/jacquard-api/src/net_aftertheinter/coolthingtwo.rs @@ -22,6 +22,44 @@ pub struct Coolthingtwo<'a> { pub status: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CoolthingtwoGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Coolthingtwo<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CoolthingtwoRecord; +impl jacquard_common::xrpc::XrpcResp for CoolthingtwoRecord { + const NSID: &'static str = "net.aftertheinter.coolthingtwo"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CoolthingtwoGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Coolthingtwo<'_> { const NSID: &'static str = "net.aftertheinter.coolthingtwo"; + type Record = CoolthingtwoRecord; +} + +impl From> for Coolthingtwo<'static> { + fn from(output: CoolthingtwoGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/beta/game/collection.rs b/crates/jacquard-api/src/net_anisota/beta/game/collection.rs index 4c19492d..eb156b7f 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/collection.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/collection.rs @@ -81,8 +81,46 @@ pub struct Collection<'a> { pub status: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CollectionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Collection<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CollectionRecord; +impl jacquard_common::xrpc::XrpcResp for CollectionRecord { + const NSID: &'static str = "net.anisota.beta.game.collection"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CollectionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Collection<'_> { const NSID: &'static str = "net.anisota.beta.game.collection"; + type Record = CollectionRecord; +} + +impl From> for Collection<'static> { + fn from(output: CollectionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Additional details about how the specimen was acquired diff --git a/crates/jacquard-api/src/net_anisota/beta/game/inventory.rs b/crates/jacquard-api/src/net_anisota/beta/game/inventory.rs index 4f06df43..65447272 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/inventory.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/inventory.rs @@ -70,8 +70,46 @@ pub struct Inventory<'a> { pub stackable: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct InventoryGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Inventory<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct InventoryRecord; +impl jacquard_common::xrpc::XrpcResp for InventoryRecord { + const NSID: &'static str = "net.anisota.beta.game.inventory"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = InventoryGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Inventory<'_> { const NSID: &'static str = "net.anisota.beta.game.inventory"; + type Record = InventoryRecord; +} + +impl From> for Inventory<'static> { + fn from(output: InventoryGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Additional details about how the item was acquired diff --git a/crates/jacquard-api/src/net_anisota/beta/game/log.rs b/crates/jacquard-api/src/net_anisota/beta/game/log.rs index 8a84e6be..b3e2de5e 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/log.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/log.rs @@ -246,8 +246,46 @@ pub struct Log<'a> { pub timestamp: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LogGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Log<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LogRecord; +impl jacquard_common::xrpc::XrpcResp for LogRecord { + const NSID: &'static str = "net.anisota.beta.game.log"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LogGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Log<'_> { const NSID: &'static str = "net.anisota.beta.game.log"; + type Record = LogRecord; +} + +impl From> for Log<'static> { + fn from(output: LogGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Additional event-specific metadata diff --git a/crates/jacquard-api/src/net_anisota/beta/game/pack.rs b/crates/jacquard-api/src/net_anisota/beta/game/pack.rs index ca474c22..40209cd4 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/pack.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/pack.rs @@ -40,8 +40,46 @@ pub struct Pack<'a> { pub total_opens: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PackGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Pack<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PackRecord; +impl jacquard_common::xrpc::XrpcResp for PackRecord { + const NSID: &'static str = "net.anisota.beta.game.pack"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PackGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Pack<'_> { const NSID: &'static str = "net.anisota.beta.game.pack"; + type Record = PackRecord; +} + +impl From> for Pack<'static> { + fn from(output: PackGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///A single pack opening entry in the history diff --git a/crates/jacquard-api/src/net_anisota/beta/game/progress.rs b/crates/jacquard-api/src/net_anisota/beta/game/progress.rs index 5082d610..a967231b 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/progress.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/progress.rs @@ -69,8 +69,46 @@ pub struct Progress<'a> { pub xp_to_next_level: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProgressGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Progress<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProgressRecord; +impl jacquard_common::xrpc::XrpcResp for ProgressRecord { + const NSID: &'static str = "net.anisota.beta.game.progress"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProgressGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Progress<'_> { const NSID: &'static str = "net.anisota.beta.game.progress"; + type Record = ProgressRecord; +} + +impl From> for Progress<'static> { + fn from(output: ProgressGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Additional metadata about this progress update diff --git a/crates/jacquard-api/src/net_anisota/beta/game/session.rs b/crates/jacquard-api/src/net_anisota/beta/game/session.rs index 4278c1d1..7e6dc66b 100644 --- a/crates/jacquard-api/src/net_anisota/beta/game/session.rs +++ b/crates/jacquard-api/src/net_anisota/beta/game/session.rs @@ -144,8 +144,46 @@ pub struct Session<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SessionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Session<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SessionRecord; +impl jacquard_common::xrpc::XrpcResp for SessionRecord { + const NSID: &'static str = "net.anisota.beta.game.session"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SessionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Session<'_> { const NSID: &'static str = "net.anisota.beta.game.session"; + type Record = SessionRecord; +} + +impl From> for Session<'static> { + fn from(output: SessionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } ///Additional session metadata diff --git a/crates/jacquard-api/src/net_anisota/feed/draft.rs b/crates/jacquard-api/src/net_anisota/feed/draft.rs index fdb390ce..b4b0eaf2 100644 --- a/crates/jacquard-api/src/net_anisota/feed/draft.rs +++ b/crates/jacquard-api/src/net_anisota/feed/draft.rs @@ -74,8 +74,46 @@ pub enum DraftEmbed<'a> { RecordWithMedia(Box>), } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DraftGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Draft<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DraftRecord; +impl jacquard_common::xrpc::XrpcResp for DraftRecord { + const NSID: &'static str = "net.anisota.feed.draft"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DraftGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Draft<'_> { const NSID: &'static str = "net.anisota.feed.draft"; + type Record = DraftRecord; +} + +impl From> for Draft<'static> { + fn from(output: DraftGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/net_anisota/feed/like.rs b/crates/jacquard-api/src/net_anisota/feed/like.rs index 835dff0b..5155b918 100644 --- a/crates/jacquard-api/src/net_anisota/feed/like.rs +++ b/crates/jacquard-api/src/net_anisota/feed/like.rs @@ -25,6 +25,44 @@ pub struct Like<'a> { pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LikeGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Like<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LikeRecord; +impl jacquard_common::xrpc::XrpcResp for LikeRecord { + const NSID: &'static str = "net.anisota.feed.like"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LikeGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Like<'_> { const NSID: &'static str = "net.anisota.feed.like"; + type Record = LikeRecord; +} + +impl From> for Like<'static> { + fn from(output: LikeGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/feed/list.rs b/crates/jacquard-api/src/net_anisota/feed/list.rs index bd5cdb80..057ac5b5 100644 --- a/crates/jacquard-api/src/net_anisota/feed/list.rs +++ b/crates/jacquard-api/src/net_anisota/feed/list.rs @@ -37,6 +37,44 @@ pub struct List<'a> { pub tags: std::option::Option>>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: List<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListRecord; +impl jacquard_common::xrpc::XrpcResp for ListRecord { + const NSID: &'static str = "net.anisota.feed.list"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for List<'_> { const NSID: &'static str = "net.anisota.feed.list"; + type Record = ListRecord; +} + +impl From> for List<'static> { + fn from(output: ListGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/feed/list_item.rs b/crates/jacquard-api/src/net_anisota/feed/list_item.rs index 105332d8..4789ea03 100644 --- a/crates/jacquard-api/src/net_anisota/feed/list_item.rs +++ b/crates/jacquard-api/src/net_anisota/feed/list_item.rs @@ -28,6 +28,44 @@ pub struct ListItem<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListItemGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: ListItem<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListItemRecord; +impl jacquard_common::xrpc::XrpcResp for ListItemRecord { + const NSID: &'static str = "net.anisota.feed.listItem"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListItemGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for ListItem<'_> { const NSID: &'static str = "net.anisota.feed.listItem"; + type Record = ListItemRecord; +} + +impl From> for ListItem<'static> { + fn from(output: ListItemGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/feed/post.rs b/crates/jacquard-api/src/net_anisota/feed/post.rs index a900e119..a92b8327 100644 --- a/crates/jacquard-api/src/net_anisota/feed/post.rs +++ b/crates/jacquard-api/src/net_anisota/feed/post.rs @@ -71,8 +71,46 @@ pub enum PostEmbed<'a> { RecordWithMedia(Box>), } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "net.anisota.feed.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "net.anisota.feed.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/net_anisota/feed/repost.rs b/crates/jacquard-api/src/net_anisota/feed/repost.rs index 9c4899fa..fa4e2adf 100644 --- a/crates/jacquard-api/src/net_anisota/feed/repost.rs +++ b/crates/jacquard-api/src/net_anisota/feed/repost.rs @@ -25,6 +25,44 @@ pub struct Repost<'a> { pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RepostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Repost<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RepostRecord; +impl jacquard_common::xrpc::XrpcResp for RepostRecord { + const NSID: &'static str = "net.anisota.feed.repost"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RepostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Repost<'_> { const NSID: &'static str = "net.anisota.feed.repost"; + type Record = RepostRecord; +} + +impl From> for Repost<'static> { + fn from(output: RepostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/graph/list_mute.rs b/crates/jacquard-api/src/net_anisota/graph/list_mute.rs index 227c552a..2a1576c3 100644 --- a/crates/jacquard-api/src/net_anisota/graph/list_mute.rs +++ b/crates/jacquard-api/src/net_anisota/graph/list_mute.rs @@ -71,6 +71,44 @@ pub struct ListMute<'a> { >, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListMuteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: ListMute<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListMuteRecord; +impl jacquard_common::xrpc::XrpcResp for ListMuteRecord { + const NSID: &'static str = "net.anisota.graph.listMute"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListMuteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for ListMute<'_> { const NSID: &'static str = "net.anisota.graph.listMute"; + type Record = ListMuteRecord; +} + +impl From> for ListMute<'static> { + fn from(output: ListMuteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_anisota/graph/mute.rs b/crates/jacquard-api/src/net_anisota/graph/mute.rs index 0706a275..60f8b407 100644 --- a/crates/jacquard-api/src/net_anisota/graph/mute.rs +++ b/crates/jacquard-api/src/net_anisota/graph/mute.rs @@ -71,6 +71,44 @@ pub struct Mute<'a> { >, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MuteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Mute<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MuteRecord; +impl jacquard_common::xrpc::XrpcResp for MuteRecord { + const NSID: &'static str = "net.anisota.graph.mute"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MuteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Mute<'_> { const NSID: &'static str = "net.anisota.graph.mute"; + type Record = MuteRecord; +} + +impl From> for Mute<'static> { + fn from(output: MuteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_bnewbold/demo/mushies.rs b/crates/jacquard-api/src/net_bnewbold/demo/mushies.rs index 471c92d7..32e0407a 100644 --- a/crates/jacquard-api/src/net_bnewbold/demo/mushies.rs +++ b/crates/jacquard-api/src/net_bnewbold/demo/mushies.rs @@ -27,6 +27,44 @@ pub struct Mushies<'a> { pub species: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MushiesGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Mushies<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MushiesRecord; +impl jacquard_common::xrpc::XrpcResp for MushiesRecord { + const NSID: &'static str = "net.bnewbold.demo.mushies"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MushiesGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Mushies<'_> { const NSID: &'static str = "net.bnewbold.demo.mushies"; + type Record = MushiesRecord; +} + +impl From> for Mushies<'static> { + fn from(output: MushiesGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_bnewbold/demo/mushroom.rs b/crates/jacquard-api/src/net_bnewbold/demo/mushroom.rs index d2478baa..c2f2230b 100644 --- a/crates/jacquard-api/src/net_bnewbold/demo/mushroom.rs +++ b/crates/jacquard-api/src/net_bnewbold/demo/mushroom.rs @@ -27,6 +27,44 @@ pub struct Mushroom<'a> { pub species: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MushroomGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Mushroom<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MushroomRecord; +impl jacquard_common::xrpc::XrpcResp for MushroomRecord { + const NSID: &'static str = "net.bnewbold.demo.mushroom"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MushroomGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Mushroom<'_> { const NSID: &'static str = "net.bnewbold.demo.mushroom"; + type Record = MushroomRecord; +} + +impl From> for Mushroom<'static> { + fn from(output: MushroomGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_bnewbold/m.rs b/crates/jacquard-api/src/net_bnewbold/m.rs index 4a4731ef..f1b3b4f1 100644 --- a/crates/jacquard-api/src/net_bnewbold/m.rs +++ b/crates/jacquard-api/src/net_bnewbold/m.rs @@ -27,6 +27,44 @@ pub struct M<'a> { pub species: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: M<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MRecord; +impl jacquard_common::xrpc::XrpcResp for MRecord { + const NSID: &'static str = "net.bnewbold.m"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for M<'_> { const NSID: &'static str = "net.bnewbold.m"; + type Record = MRecord; +} + +impl From> for M<'static> { + fn from(output: MGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_mmatt/right/now.rs b/crates/jacquard-api/src/net_mmatt/right/now.rs index e9128b75..8a572389 100644 --- a/crates/jacquard-api/src/net_mmatt/right/now.rs +++ b/crates/jacquard-api/src/net_mmatt/right/now.rs @@ -29,6 +29,44 @@ pub struct Now<'a> { pub text: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct NowGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Now<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct NowRecord; +impl jacquard_common::xrpc::XrpcResp for NowRecord { + const NSID: &'static str = "net.mmatt.right.now"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = NowGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Now<'_> { const NSID: &'static str = "net.mmatt.right.now"; + type Record = NowRecord; +} + +impl From> for Now<'static> { + fn from(output: NowGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/net_mmatt/vitals/car.rs b/crates/jacquard-api/src/net_mmatt/vitals/car.rs index 6e38900e..ed1b8084 100644 --- a/crates/jacquard-api/src/net_mmatt/vitals/car.rs +++ b/crates/jacquard-api/src/net_mmatt/vitals/car.rs @@ -42,6 +42,44 @@ pub struct Car<'a> { pub created_at: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CarGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Car<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CarRecord; +impl jacquard_common::xrpc::XrpcResp for CarRecord { + const NSID: &'static str = "net.mmatt.vitals.car"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CarGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Car<'_> { const NSID: &'static str = "net.mmatt.vitals.car"; + type Record = CarRecord; +} + +impl From> for Car<'static> { + fn from(output: CarGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/network_slices/actor/profile.rs b/crates/jacquard-api/src/network_slices/actor/profile.rs index 7e331b46..09177f5f 100644 --- a/crates/jacquard-api/src/network_slices/actor/profile.rs +++ b/crates/jacquard-api/src/network_slices/actor/profile.rs @@ -33,6 +33,44 @@ pub struct Profile<'a> { pub display_name: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "network.slices.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "network.slices.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/network_slices/lexicon.rs b/crates/jacquard-api/src/network_slices/lexicon.rs index 5c7a0450..7c3c96fe 100644 --- a/crates/jacquard-api/src/network_slices/lexicon.rs +++ b/crates/jacquard-api/src/network_slices/lexicon.rs @@ -40,6 +40,44 @@ pub struct Lexicon<'a> { pub updated_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LexiconGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Lexicon<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LexiconRecord; +impl jacquard_common::xrpc::XrpcResp for LexiconRecord { + const NSID: &'static str = "network.slices.lexicon"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LexiconGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Lexicon<'_> { const NSID: &'static str = "network.slices.lexicon"; + type Record = LexiconRecord; +} + +impl From> for Lexicon<'static> { + fn from(output: LexiconGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/network_slices/slice.rs b/crates/jacquard-api/src/network_slices/slice.rs index 6c817c9c..0a5e2217 100644 --- a/crates/jacquard-api/src/network_slices/slice.rs +++ b/crates/jacquard-api/src/network_slices/slice.rs @@ -44,8 +44,46 @@ pub struct Slice<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SliceGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Slice<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SliceRecord; +impl jacquard_common::xrpc::XrpcResp for SliceRecord { + const NSID: &'static str = "network.slices.slice"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SliceGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Slice<'_> { const NSID: &'static str = "network.slices.slice"; + type Record = SliceRecord; +} + +impl From> for Slice<'static> { + fn from(output: SliceGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/network_slices/waitlist/invite.rs b/crates/jacquard-api/src/network_slices/waitlist/invite.rs index b0c07f5b..42f53a93 100644 --- a/crates/jacquard-api/src/network_slices/waitlist/invite.rs +++ b/crates/jacquard-api/src/network_slices/waitlist/invite.rs @@ -31,6 +31,44 @@ pub struct Invite<'a> { pub slice: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct InviteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Invite<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct InviteRecord; +impl jacquard_common::xrpc::XrpcResp for InviteRecord { + const NSID: &'static str = "network.slices.waitlist.invite"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = InviteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Invite<'_> { const NSID: &'static str = "network.slices.waitlist.invite"; + type Record = InviteRecord; +} + +impl From> for Invite<'static> { + fn from(output: InviteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/network_slices/waitlist/request.rs b/crates/jacquard-api/src/network_slices/waitlist/request.rs index ff44f6aa..423646ed 100644 --- a/crates/jacquard-api/src/network_slices/waitlist/request.rs +++ b/crates/jacquard-api/src/network_slices/waitlist/request.rs @@ -25,6 +25,44 @@ pub struct Request<'a> { pub slice: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RequestGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Request<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RequestRecord; +impl jacquard_common::xrpc::XrpcResp for RequestRecord { + const NSID: &'static str = "network.slices.waitlist.request"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RequestGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Request<'_> { const NSID: &'static str = "network.slices.waitlist.request"; + type Record = RequestRecord; +} + +impl From> for Request<'static> { + fn from(output: RequestGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/org_devcon/event/test.rs b/crates/jacquard-api/src/org_devcon/event/test.rs index 2dc9d3ff..e31ad81b 100644 --- a/crates/jacquard-api/src/org_devcon/event/test.rs +++ b/crates/jacquard-api/src/org_devcon/event/test.rs @@ -40,6 +40,44 @@ pub struct Test<'a> { pub url: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct TestGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Test<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct TestRecord; +impl jacquard_common::xrpc::XrpcResp for TestRecord { + const NSID: &'static str = "org.devcon.event.test"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = TestGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Test<'_> { const NSID: &'static str = "org.devcon.event.test"; + type Record = TestRecord; +} + +impl From> for Test<'static> { + fn from(output: TestGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/org_robocracy/demo/fungus.rs b/crates/jacquard-api/src/org_robocracy/demo/fungus.rs index c3cb89fc..45615d56 100644 --- a/crates/jacquard-api/src/org_robocracy/demo/fungus.rs +++ b/crates/jacquard-api/src/org_robocracy/demo/fungus.rs @@ -27,6 +27,44 @@ pub struct Fungus<'a> { pub species: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct FungusGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Fungus<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct FungusRecord; +impl jacquard_common::xrpc::XrpcResp for FungusRecord { + const NSID: &'static str = "org.robocracy.demo.fungus"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = FungusGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Fungus<'_> { const NSID: &'static str = "org.robocracy.demo.fungus"; + type Record = FungusRecord; +} + +impl From> for Fungus<'static> { + fn from(output: FungusGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/org_robocracy/demo/mushies.rs b/crates/jacquard-api/src/org_robocracy/demo/mushies.rs index f8fd025a..4750157a 100644 --- a/crates/jacquard-api/src/org_robocracy/demo/mushies.rs +++ b/crates/jacquard-api/src/org_robocracy/demo/mushies.rs @@ -27,6 +27,44 @@ pub struct Mushies<'a> { pub species: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MushiesGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Mushies<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MushiesRecord; +impl jacquard_common::xrpc::XrpcResp for MushiesRecord { + const NSID: &'static str = "org.robocracy.demo.mushies"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MushiesGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Mushies<'_> { const NSID: &'static str = "org.robocracy.demo.mushies"; + type Record = MushiesRecord; +} + +impl From> for Mushies<'static> { + fn from(output: MushiesGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_atwork/endorsement.rs b/crates/jacquard-api/src/place_atwork/endorsement.rs index 6d643400..171c6f41 100644 --- a/crates/jacquard-api/src/place_atwork/endorsement.rs +++ b/crates/jacquard-api/src/place_atwork/endorsement.rs @@ -37,6 +37,44 @@ pub struct Endorsement<'a> { pub text: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct EndorsementGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Endorsement<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct EndorsementRecord; +impl jacquard_common::xrpc::XrpcResp for EndorsementRecord { + const NSID: &'static str = "place.atwork.endorsement"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = EndorsementGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Endorsement<'_> { const NSID: &'static str = "place.atwork.endorsement"; + type Record = EndorsementRecord; +} + +impl From> for Endorsement<'static> { + fn from(output: EndorsementGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_atwork/endorsement_proof.rs b/crates/jacquard-api/src/place_atwork/endorsement_proof.rs index fe6525aa..ed6c1eed 100644 --- a/crates/jacquard-api/src/place_atwork/endorsement_proof.rs +++ b/crates/jacquard-api/src/place_atwork/endorsement_proof.rs @@ -23,6 +23,44 @@ pub struct EndorsementProof<'a> { pub cid: jacquard_common::types::string::Cid<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct EndorsementProofGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: EndorsementProof<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct EndorsementProofRecord; +impl jacquard_common::xrpc::XrpcResp for EndorsementProofRecord { + const NSID: &'static str = "place.atwork.endorsementProof"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = EndorsementProofGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for EndorsementProof<'_> { const NSID: &'static str = "place.atwork.endorsementProof"; + type Record = EndorsementProofRecord; +} + +impl From> for EndorsementProof<'static> { + fn from(output: EndorsementProofGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_atwork/listing.rs b/crates/jacquard-api/src/place_atwork/listing.rs index f0d6dee1..65c50e65 100644 --- a/crates/jacquard-api/src/place_atwork/listing.rs +++ b/crates/jacquard-api/src/place_atwork/listing.rs @@ -48,6 +48,44 @@ pub struct Listing<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ListingGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Listing<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ListingRecord; +impl jacquard_common::xrpc::XrpcResp for ListingRecord { + const NSID: &'static str = "place.atwork.listing"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ListingGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Listing<'_> { const NSID: &'static str = "place.atwork.listing"; + type Record = ListingRecord; +} + +impl From> for Listing<'static> { + fn from(output: ListingGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_atwork/profile.rs b/crates/jacquard-api/src/place_atwork/profile.rs index 6468f521..3f492c7b 100644 --- a/crates/jacquard-api/src/place_atwork/profile.rs +++ b/crates/jacquard-api/src/place_atwork/profile.rs @@ -88,6 +88,44 @@ pub struct Profile<'a> { pub status: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "place.atwork.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "place.atwork.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_stream/chat/gate.rs b/crates/jacquard-api/src/place_stream/chat/gate.rs index 9d47e02a..a3aa926e 100644 --- a/crates/jacquard-api/src/place_stream/chat/gate.rs +++ b/crates/jacquard-api/src/place_stream/chat/gate.rs @@ -23,6 +23,44 @@ pub struct Gate<'a> { pub hidden_message: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GateGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Gate<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GateRecord; +impl jacquard_common::xrpc::XrpcResp for GateRecord { + const NSID: &'static str = "place.stream.chat.gate"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GateGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Gate<'_> { const NSID: &'static str = "place.stream.chat.gate"; + type Record = GateRecord; +} + +impl From> for Gate<'static> { + fn from(output: GateGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_stream/chat/message.rs b/crates/jacquard-api/src/place_stream/chat/message.rs index 2378d22f..325816b0 100644 --- a/crates/jacquard-api/src/place_stream/chat/message.rs +++ b/crates/jacquard-api/src/place_stream/chat/message.rs @@ -37,8 +37,46 @@ pub struct Message<'a> { pub text: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MessageGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Message<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MessageRecord; +impl jacquard_common::xrpc::XrpcResp for MessageRecord { + const NSID: &'static str = "place.stream.chat.message"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MessageGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Message<'_> { const NSID: &'static str = "place.stream.chat.message"; + type Record = MessageRecord; +} + +impl From> for Message<'static> { + fn from(output: MessageGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/place_stream/chat/profile.rs b/crates/jacquard-api/src/place_stream/chat/profile.rs index ac39dbc3..17f69d42 100644 --- a/crates/jacquard-api/src/place_stream/chat/profile.rs +++ b/crates/jacquard-api/src/place_stream/chat/profile.rs @@ -41,6 +41,44 @@ pub struct Profile<'a> { pub color: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "place.stream.chat.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "place.stream.chat.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_stream/key.rs b/crates/jacquard-api/src/place_stream/key.rs index 39c5af12..71668fdd 100644 --- a/crates/jacquard-api/src/place_stream/key.rs +++ b/crates/jacquard-api/src/place_stream/key.rs @@ -29,6 +29,44 @@ pub struct Key<'a> { pub signing_key: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct KeyGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Key<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct KeyRecord; +impl jacquard_common::xrpc::XrpcResp for KeyRecord { + const NSID: &'static str = "place.stream.key"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = KeyGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Key<'_> { const NSID: &'static str = "place.stream.key"; + type Record = KeyRecord; +} + +impl From> for Key<'static> { + fn from(output: KeyGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/place_stream/livestream.rs b/crates/jacquard-api/src/place_stream/livestream.rs index ee3cedac..c58f55fd 100644 --- a/crates/jacquard-api/src/place_stream/livestream.rs +++ b/crates/jacquard-api/src/place_stream/livestream.rs @@ -73,8 +73,46 @@ pub struct Livestream<'a> { pub url: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct LivestreamGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Livestream<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct LivestreamRecord; +impl jacquard_common::xrpc::XrpcResp for LivestreamRecord { + const NSID: &'static str = "place.stream.livestream"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = LivestreamGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Livestream<'_> { const NSID: &'static str = "place.stream.livestream"; + type Record = LivestreamRecord; +} + +impl From> for Livestream<'static> { + fn from(output: LivestreamGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/place_stream/segment.rs b/crates/jacquard-api/src/place_stream/segment.rs index 480f7f32..11aabef6 100644 --- a/crates/jacquard-api/src/place_stream/segment.rs +++ b/crates/jacquard-api/src/place_stream/segment.rs @@ -76,8 +76,46 @@ pub struct Segment<'a> { pub video: std::option::Option>>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SegmentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Segment<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SegmentRecord; +impl jacquard_common::xrpc::XrpcResp for SegmentRecord { + const NSID: &'static str = "place.stream.segment"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SegmentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Segment<'_> { const NSID: &'static str = "place.stream.segment"; + type Record = SegmentRecord; +} + +impl From> for Segment<'static> { + fn from(output: SegmentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/place_stream/server/settings.rs b/crates/jacquard-api/src/place_stream/server/settings.rs index ef20a916..b564ccae 100644 --- a/crates/jacquard-api/src/place_stream/server/settings.rs +++ b/crates/jacquard-api/src/place_stream/server/settings.rs @@ -23,6 +23,44 @@ pub struct Settings<'a> { pub debug_recording: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SettingsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Settings<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SettingsRecord; +impl jacquard_common::xrpc::XrpcResp for SettingsRecord { + const NSID: &'static str = "place.stream.server.settings"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SettingsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Settings<'_> { const NSID: &'static str = "place.stream.server.settings"; + type Record = SettingsRecord; +} + +impl From> for Settings<'static> { + fn from(output: SettingsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/pub_leaflet/comment.rs b/crates/jacquard-api/src/pub_leaflet/comment.rs index 9dbcce55..1d81b878 100644 --- a/crates/jacquard-api/src/pub_leaflet/comment.rs +++ b/crates/jacquard-api/src/pub_leaflet/comment.rs @@ -54,8 +54,46 @@ pub struct Comment<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CommentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Comment<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CommentRecord; +impl jacquard_common::xrpc::XrpcResp for CommentRecord { + const NSID: &'static str = "pub.leaflet.comment"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CommentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Comment<'_> { const NSID: &'static str = "pub.leaflet.comment"; + type Record = CommentRecord; +} + +impl From> for Comment<'static> { + fn from(output: CommentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/pub_leaflet/document.rs b/crates/jacquard-api/src/pub_leaflet/document.rs index 0ba39b19..52cd1fd4 100644 --- a/crates/jacquard-api/src/pub_leaflet/document.rs +++ b/crates/jacquard-api/src/pub_leaflet/document.rs @@ -38,6 +38,44 @@ pub struct Document<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DocumentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Document<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DocumentRecord; +impl jacquard_common::xrpc::XrpcResp for DocumentRecord { + const NSID: &'static str = "pub.leaflet.document"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DocumentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Document<'_> { const NSID: &'static str = "pub.leaflet.document"; + type Record = DocumentRecord; +} + +impl From> for Document<'static> { + fn from(output: DocumentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/pub_leaflet/graph/subscription.rs b/crates/jacquard-api/src/pub_leaflet/graph/subscription.rs index cb5cc9df..3687a152 100644 --- a/crates/jacquard-api/src/pub_leaflet/graph/subscription.rs +++ b/crates/jacquard-api/src/pub_leaflet/graph/subscription.rs @@ -22,6 +22,44 @@ pub struct Subscription<'a> { pub publication: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SubscriptionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Subscription<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SubscriptionRecord; +impl jacquard_common::xrpc::XrpcResp for SubscriptionRecord { + const NSID: &'static str = "pub.leaflet.graph.subscription"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SubscriptionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Subscription<'_> { const NSID: &'static str = "pub.leaflet.graph.subscription"; + type Record = SubscriptionRecord; +} + +impl From> for Subscription<'static> { + fn from(output: SubscriptionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/pub_leaflet/publication.rs b/crates/jacquard-api/src/pub_leaflet/publication.rs index 4120354c..99c39cbc 100644 --- a/crates/jacquard-api/src/pub_leaflet/publication.rs +++ b/crates/jacquard-api/src/pub_leaflet/publication.rs @@ -39,8 +39,46 @@ pub struct Publication<'a> { pub theme: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PublicationGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Publication<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PublicationRecord; +impl jacquard_common::xrpc::XrpcResp for PublicationRecord { + const NSID: &'static str = "pub.leaflet.publication"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PublicationGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Publication<'_> { const NSID: &'static str = "pub.leaflet.publication"; + type Record = PublicationRecord; +} + +impl From> for Publication<'static> { + fn from(output: PublicationGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/actor/profile.rs b/crates/jacquard-api/src/sh_tangled/actor/profile.rs index 7ed535a3..8361c8d3 100644 --- a/crates/jacquard-api/src/sh_tangled/actor/profile.rs +++ b/crates/jacquard-api/src/sh_tangled/actor/profile.rs @@ -42,6 +42,44 @@ pub struct Profile<'a> { pub stats: std::option::Option>>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "sh.tangled.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "sh.tangled.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/feed/reaction.rs b/crates/jacquard-api/src/sh_tangled/feed/reaction.rs index 141386b7..46c9610c 100644 --- a/crates/jacquard-api/src/sh_tangled/feed/reaction.rs +++ b/crates/jacquard-api/src/sh_tangled/feed/reaction.rs @@ -24,6 +24,44 @@ pub struct Reaction<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ReactionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Reaction<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ReactionRecord; +impl jacquard_common::xrpc::XrpcResp for ReactionRecord { + const NSID: &'static str = "sh.tangled.feed.reaction"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ReactionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Reaction<'_> { const NSID: &'static str = "sh.tangled.feed.reaction"; + type Record = ReactionRecord; +} + +impl From> for Reaction<'static> { + fn from(output: ReactionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/feed/star.rs b/crates/jacquard-api/src/sh_tangled/feed/star.rs index 414f7cc5..9fa93182 100644 --- a/crates/jacquard-api/src/sh_tangled/feed/star.rs +++ b/crates/jacquard-api/src/sh_tangled/feed/star.rs @@ -22,6 +22,44 @@ pub struct Star<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StarGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Star<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StarRecord; +impl jacquard_common::xrpc::XrpcResp for StarRecord { + const NSID: &'static str = "sh.tangled.feed.star"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StarGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Star<'_> { const NSID: &'static str = "sh.tangled.feed.star"; + type Record = StarRecord; +} + +impl From> for Star<'static> { + fn from(output: StarGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/git/ref_update.rs b/crates/jacquard-api/src/sh_tangled/git/ref_update.rs index cbe3176a..1c3ce28d 100644 --- a/crates/jacquard-api/src/sh_tangled/git/ref_update.rs +++ b/crates/jacquard-api/src/sh_tangled/git/ref_update.rs @@ -112,8 +112,46 @@ pub struct RefUpdate<'a> { pub repo_name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RefUpdateGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: RefUpdate<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RefUpdateRecord; +impl jacquard_common::xrpc::XrpcResp for RefUpdateRecord { + const NSID: &'static str = "sh.tangled.git.refUpdate"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RefUpdateGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for RefUpdate<'_> { const NSID: &'static str = "sh.tangled.git.refUpdate"; + type Record = RefUpdateRecord; +} + +impl From> for RefUpdate<'static> { + fn from(output: RefUpdateGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/graph/follow.rs b/crates/jacquard-api/src/sh_tangled/graph/follow.rs index 902f1e93..002f2f37 100644 --- a/crates/jacquard-api/src/sh_tangled/graph/follow.rs +++ b/crates/jacquard-api/src/sh_tangled/graph/follow.rs @@ -22,6 +22,44 @@ pub struct Follow<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct FollowGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Follow<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct FollowRecord; +impl jacquard_common::xrpc::XrpcResp for FollowRecord { + const NSID: &'static str = "sh.tangled.graph.follow"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = FollowGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Follow<'_> { const NSID: &'static str = "sh.tangled.graph.follow"; + type Record = FollowRecord; +} + +impl From> for Follow<'static> { + fn from(output: FollowGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/knot.rs b/crates/jacquard-api/src/sh_tangled/knot.rs index dcc14bfb..e10a6cb9 100644 --- a/crates/jacquard-api/src/sh_tangled/knot.rs +++ b/crates/jacquard-api/src/sh_tangled/knot.rs @@ -24,6 +24,44 @@ pub struct Knot<'a> { pub created_at: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct KnotGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Knot<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct KnotRecord; +impl jacquard_common::xrpc::XrpcResp for KnotRecord { + const NSID: &'static str = "sh.tangled.knot"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = KnotGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Knot<'_> { const NSID: &'static str = "sh.tangled.knot"; + type Record = KnotRecord; +} + +impl From> for Knot<'static> { + fn from(output: KnotGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/knot/member.rs b/crates/jacquard-api/src/sh_tangled/knot/member.rs index 69bec785..aa99f98d 100644 --- a/crates/jacquard-api/src/sh_tangled/knot/member.rs +++ b/crates/jacquard-api/src/sh_tangled/knot/member.rs @@ -25,6 +25,44 @@ pub struct Member<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MemberGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Member<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MemberRecord; +impl jacquard_common::xrpc::XrpcResp for MemberRecord { + const NSID: &'static str = "sh.tangled.knot.member"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MemberGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Member<'_> { const NSID: &'static str = "sh.tangled.knot.member"; + type Record = MemberRecord; +} + +impl From> for Member<'static> { + fn from(output: MemberGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/label/definition.rs b/crates/jacquard-api/src/sh_tangled/label/definition.rs index 8764a204..3a2d74c3 100644 --- a/crates/jacquard-api/src/sh_tangled/label/definition.rs +++ b/crates/jacquard-api/src/sh_tangled/label/definition.rs @@ -36,8 +36,46 @@ pub struct Definition<'a> { pub value_type: crate::sh_tangled::label::definition::ValueType<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DefinitionGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Definition<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DefinitionRecord; +impl jacquard_common::xrpc::XrpcResp for DefinitionRecord { + const NSID: &'static str = "sh.tangled.label.definition"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DefinitionGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Definition<'_> { const NSID: &'static str = "sh.tangled.label.definition"; + type Record = DefinitionRecord; +} + +impl From> for Definition<'static> { + fn from(output: DefinitionGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/label/op.rs b/crates/jacquard-api/src/sh_tangled/label/op.rs index 4612638a..cb4a7a11 100644 --- a/crates/jacquard-api/src/sh_tangled/label/op.rs +++ b/crates/jacquard-api/src/sh_tangled/label/op.rs @@ -27,8 +27,46 @@ pub struct Op<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct OpGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Op<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct OpRecord; +impl jacquard_common::xrpc::XrpcResp for OpRecord { + const NSID: &'static str = "sh.tangled.label.op"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = OpGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Op<'_> { const NSID: &'static str = "sh.tangled.label.op"; + type Record = OpRecord; +} + +impl From> for Op<'static> { + fn from(output: OpGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/pipeline.rs b/crates/jacquard-api/src/sh_tangled/pipeline.rs index 89c89b05..1ce6826b 100644 --- a/crates/jacquard-api/src/sh_tangled/pipeline.rs +++ b/crates/jacquard-api/src/sh_tangled/pipeline.rs @@ -42,8 +42,46 @@ pub struct Pipeline<'a> { pub workflows: Vec>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PipelineGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Pipeline<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PipelineRecord; +impl jacquard_common::xrpc::XrpcResp for PipelineRecord { + const NSID: &'static str = "sh.tangled.pipeline"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PipelineGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Pipeline<'_> { const NSID: &'static str = "sh.tangled.pipeline"; + type Record = PipelineRecord; +} + +impl From> for Pipeline<'static> { + fn from(output: PipelineGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/pipeline/status.rs b/crates/jacquard-api/src/sh_tangled/pipeline/status.rs index aa875e48..26448e2b 100644 --- a/crates/jacquard-api/src/sh_tangled/pipeline/status.rs +++ b/crates/jacquard-api/src/sh_tangled/pipeline/status.rs @@ -37,6 +37,44 @@ pub struct Status<'a> { pub workflow: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatusGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Status<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatusRecord; +impl jacquard_common::xrpc::XrpcResp for StatusRecord { + const NSID: &'static str = "sh.tangled.pipeline.status"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatusGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Status<'_> { const NSID: &'static str = "sh.tangled.pipeline.status"; + type Record = StatusRecord; +} + +impl From> for Status<'static> { + fn from(output: StatusGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/public_key.rs b/crates/jacquard-api/src/sh_tangled/public_key.rs index 3cab62d8..d4101a9b 100644 --- a/crates/jacquard-api/src/sh_tangled/public_key.rs +++ b/crates/jacquard-api/src/sh_tangled/public_key.rs @@ -27,6 +27,44 @@ pub struct PublicKey<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PublicKeyGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: PublicKey<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PublicKeyRecord; +impl jacquard_common::xrpc::XrpcResp for PublicKeyRecord { + const NSID: &'static str = "sh.tangled.publicKey"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PublicKeyGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for PublicKey<'_> { const NSID: &'static str = "sh.tangled.publicKey"; + type Record = PublicKeyRecord; +} + +impl From> for PublicKey<'static> { + fn from(output: PublicKeyGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo.rs b/crates/jacquard-api/src/sh_tangled/repo.rs index 7057fc8a..b38954e8 100644 --- a/crates/jacquard-api/src/sh_tangled/repo.rs +++ b/crates/jacquard-api/src/sh_tangled/repo.rs @@ -69,6 +69,44 @@ pub struct Repo<'a> { pub spindle: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RepoGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Repo<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RepoRecord; +impl jacquard_common::xrpc::XrpcResp for RepoRecord { + const NSID: &'static str = "sh.tangled.repo"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RepoGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Repo<'_> { const NSID: &'static str = "sh.tangled.repo"; + type Record = RepoRecord; +} + +impl From> for Repo<'static> { + fn from(output: RepoGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/artifact.rs b/crates/jacquard-api/src/sh_tangled/repo/artifact.rs index a34bfec0..47b18ae5 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/artifact.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/artifact.rs @@ -32,6 +32,44 @@ pub struct Artifact<'a> { pub tag: bytes::Bytes, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ArtifactGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Artifact<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ArtifactRecord; +impl jacquard_common::xrpc::XrpcResp for ArtifactRecord { + const NSID: &'static str = "sh.tangled.repo.artifact"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ArtifactGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Artifact<'_> { const NSID: &'static str = "sh.tangled.repo.artifact"; + type Record = ArtifactRecord; +} + +impl From> for Artifact<'static> { + fn from(output: ArtifactGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/collaborator.rs b/crates/jacquard-api/src/sh_tangled/repo/collaborator.rs index fe485551..1171fd68 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/collaborator.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/collaborator.rs @@ -25,6 +25,44 @@ pub struct Collaborator<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CollaboratorGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Collaborator<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CollaboratorRecord; +impl jacquard_common::xrpc::XrpcResp for CollaboratorRecord { + const NSID: &'static str = "sh.tangled.repo.collaborator"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CollaboratorGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Collaborator<'_> { const NSID: &'static str = "sh.tangled.repo.collaborator"; + type Record = CollaboratorRecord; +} + +impl From> for Collaborator<'static> { + fn from(output: CollaboratorGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/issue.rs b/crates/jacquard-api/src/sh_tangled/repo/issue.rs index 99e729de..529e90a2 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/issue.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/issue.rs @@ -30,6 +30,44 @@ pub struct Issue<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct IssueGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Issue<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct IssueRecord; +impl jacquard_common::xrpc::XrpcResp for IssueRecord { + const NSID: &'static str = "sh.tangled.repo.issue"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = IssueGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Issue<'_> { const NSID: &'static str = "sh.tangled.repo.issue"; + type Record = IssueRecord; +} + +impl From> for Issue<'static> { + fn from(output: IssueGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/issue/comment.rs b/crates/jacquard-api/src/sh_tangled/repo/issue/comment.rs index a1252e08..458dd357 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/issue/comment.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/issue/comment.rs @@ -27,6 +27,44 @@ pub struct Comment<'a> { pub reply_to: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CommentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Comment<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CommentRecord; +impl jacquard_common::xrpc::XrpcResp for CommentRecord { + const NSID: &'static str = "sh.tangled.repo.issue.comment"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CommentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Comment<'_> { const NSID: &'static str = "sh.tangled.repo.issue.comment"; + type Record = CommentRecord; +} + +impl From> for Comment<'static> { + fn from(output: CommentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/issue/state.rs b/crates/jacquard-api/src/sh_tangled/repo/issue/state.rs index 0780af5b..eb5d31ca 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/issue/state.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/issue/state.rs @@ -27,6 +27,44 @@ pub struct State<'a> { pub state: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StateGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: State<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StateRecord; +impl jacquard_common::xrpc::XrpcResp for StateRecord { + const NSID: &'static str = "sh.tangled.repo.issue.state"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StateGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for State<'_> { const NSID: &'static str = "sh.tangled.repo.issue.state"; + type Record = StateRecord; +} + +impl From> for State<'static> { + fn from(output: StateGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/pull.rs b/crates/jacquard-api/src/sh_tangled/repo/pull.rs index 1efdd34d..d8bf1968 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/pull.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/pull.rs @@ -35,8 +35,46 @@ pub struct Pull<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PullGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Pull<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PullRecord; +impl jacquard_common::xrpc::XrpcResp for PullRecord { + const NSID: &'static str = "sh.tangled.repo.pull"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PullGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Pull<'_> { const NSID: &'static str = "sh.tangled.repo.pull"; + type Record = PullRecord; +} + +impl From> for Pull<'static> { + fn from(output: PullGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_tangled/repo/pull/comment.rs b/crates/jacquard-api/src/sh_tangled/repo/pull/comment.rs index 33e3ed17..6ca860a1 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/pull/comment.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/pull/comment.rs @@ -24,6 +24,44 @@ pub struct Comment<'a> { pub pull: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CommentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Comment<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CommentRecord; +impl jacquard_common::xrpc::XrpcResp for CommentRecord { + const NSID: &'static str = "sh.tangled.repo.pull.comment"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CommentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Comment<'_> { const NSID: &'static str = "sh.tangled.repo.pull.comment"; + type Record = CommentRecord; +} + +impl From> for Comment<'static> { + fn from(output: CommentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/repo/pull/status.rs b/crates/jacquard-api/src/sh_tangled/repo/pull/status.rs index 95efea72..569538b4 100644 --- a/crates/jacquard-api/src/sh_tangled/repo/pull/status.rs +++ b/crates/jacquard-api/src/sh_tangled/repo/pull/status.rs @@ -28,6 +28,44 @@ pub struct Status<'a> { pub status: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StatusGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Status<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StatusRecord; +impl jacquard_common::xrpc::XrpcResp for StatusRecord { + const NSID: &'static str = "sh.tangled.repo.pull.status"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StatusGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Status<'_> { const NSID: &'static str = "sh.tangled.repo.pull.status"; + type Record = StatusRecord; +} + +impl From> for Status<'static> { + fn from(output: StatusGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/spindle.rs b/crates/jacquard-api/src/sh_tangled/spindle.rs index b9ec748c..03c1a0a8 100644 --- a/crates/jacquard-api/src/sh_tangled/spindle.rs +++ b/crates/jacquard-api/src/sh_tangled/spindle.rs @@ -22,6 +22,44 @@ pub struct Spindle<'a> { pub created_at: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct SpindleGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Spindle<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct SpindleRecord; +impl jacquard_common::xrpc::XrpcResp for SpindleRecord { + const NSID: &'static str = "sh.tangled.spindle"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = SpindleGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Spindle<'_> { const NSID: &'static str = "sh.tangled.spindle"; + type Record = SpindleRecord; +} + +impl From> for Spindle<'static> { + fn from(output: SpindleGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/spindle/member.rs b/crates/jacquard-api/src/sh_tangled/spindle/member.rs index da19a5fb..e24481e5 100644 --- a/crates/jacquard-api/src/sh_tangled/spindle/member.rs +++ b/crates/jacquard-api/src/sh_tangled/spindle/member.rs @@ -25,6 +25,44 @@ pub struct Member<'a> { pub subject: jacquard_common::types::string::Did<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MemberGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Member<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MemberRecord; +impl jacquard_common::xrpc::XrpcResp for MemberRecord { + const NSID: &'static str = "sh.tangled.spindle.member"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MemberGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Member<'_> { const NSID: &'static str = "sh.tangled.spindle.member"; + type Record = MemberRecord; +} + +impl From> for Member<'static> { + fn from(output: MemberGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_tangled/string.rs b/crates/jacquard-api/src/sh_tangled/string.rs index e1374195..1e318174 100644 --- a/crates/jacquard-api/src/sh_tangled/string.rs +++ b/crates/jacquard-api/src/sh_tangled/string.rs @@ -26,6 +26,44 @@ pub struct String<'a> { pub filename: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct StringGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: String<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct StringRecord; +impl jacquard_common::xrpc::XrpcResp for StringRecord { + const NSID: &'static str = "sh.tangled.string"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = StringGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for String<'_> { const NSID: &'static str = "sh.tangled.string"; + type Record = StringRecord; +} + +impl From> for String<'static> { + fn from(output: StringGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/actor/profile.rs b/crates/jacquard-api/src/sh_weaver/actor/profile.rs index 531b7f3c..e96ed2c4 100644 --- a/crates/jacquard-api/src/sh_weaver/actor/profile.rs +++ b/crates/jacquard-api/src/sh_weaver/actor/profile.rs @@ -57,8 +57,46 @@ pub struct Profile<'a> { pub tangled: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "sh.weaver.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "sh.weaver.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } pub type PronounsList<'a> = Vec>; \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/edit/cursor.rs b/crates/jacquard-api/src/sh_weaver/edit/cursor.rs index 47660f1f..1cf469ff 100644 --- a/crates/jacquard-api/src/sh_weaver/edit/cursor.rs +++ b/crates/jacquard-api/src/sh_weaver/edit/cursor.rs @@ -95,8 +95,46 @@ pub struct Cursor<'a> { pub side: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct CursorGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Cursor<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct CursorRecord; +impl jacquard_common::xrpc::XrpcResp for CursorRecord { + const NSID: &'static str = "sh.weaver.edit.cursor"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = CursorGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Cursor<'_> { const NSID: &'static str = "sh.weaver.edit.cursor"; + type Record = CursorRecord; +} + +impl From> for Cursor<'static> { + fn from(output: CursorGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/sh_weaver/edit/diff.rs b/crates/jacquard-api/src/sh_weaver/edit/diff.rs index 7f067c8f..0a0c5443 100644 --- a/crates/jacquard-api/src/sh_weaver/edit/diff.rs +++ b/crates/jacquard-api/src/sh_weaver/edit/diff.rs @@ -26,6 +26,44 @@ pub struct Diff<'a> { pub snapshot: jacquard_common::types::blob::Blob<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct DiffGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Diff<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct DiffRecord; +impl jacquard_common::xrpc::XrpcResp for DiffRecord { + const NSID: &'static str = "sh.weaver.edit.diff"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = DiffGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Diff<'_> { const NSID: &'static str = "sh.weaver.edit.diff"; + type Record = DiffRecord; +} + +impl From> for Diff<'static> { + fn from(output: DiffGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/edit/root.rs b/crates/jacquard-api/src/sh_weaver/edit/root.rs index 36ec344f..a084b602 100644 --- a/crates/jacquard-api/src/sh_weaver/edit/root.rs +++ b/crates/jacquard-api/src/sh_weaver/edit/root.rs @@ -28,6 +28,44 @@ pub struct Root<'a> { pub uri: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RootGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Root<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RootRecord; +impl jacquard_common::xrpc::XrpcResp for RootRecord { + const NSID: &'static str = "sh.weaver.edit.root"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RootGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Root<'_> { const NSID: &'static str = "sh.weaver.edit.root"; + type Record = RootRecord; +} + +impl From> for Root<'static> { + fn from(output: RootGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/notebook/authors.rs b/crates/jacquard-api/src/sh_weaver/notebook/authors.rs index 6a7d8435..3c4e0b06 100644 --- a/crates/jacquard-api/src/sh_weaver/notebook/authors.rs +++ b/crates/jacquard-api/src/sh_weaver/notebook/authors.rs @@ -63,6 +63,44 @@ pub struct Authors<'a> { pub created_at: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct AuthorsGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Authors<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct AuthorsRecord; +impl jacquard_common::xrpc::XrpcResp for AuthorsRecord { + const NSID: &'static str = "sh.weaver.notebook.authors"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = AuthorsGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Authors<'_> { const NSID: &'static str = "sh.weaver.notebook.authors"; + type Record = AuthorsRecord; +} + +impl From> for Authors<'static> { + fn from(output: AuthorsGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/notebook/book.rs b/crates/jacquard-api/src/sh_weaver/notebook/book.rs index 1b38079c..79574713 100644 --- a/crates/jacquard-api/src/sh_weaver/notebook/book.rs +++ b/crates/jacquard-api/src/sh_weaver/notebook/book.rs @@ -33,6 +33,44 @@ pub struct Book<'a> { pub title: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BookGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Book<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BookRecord; +impl jacquard_common::xrpc::XrpcResp for BookRecord { + const NSID: &'static str = "sh.weaver.notebook.book"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BookGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Book<'_> { const NSID: &'static str = "sh.weaver.notebook.book"; + type Record = BookRecord; +} + +impl From> for Book<'static> { + fn from(output: BookGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/notebook/chapter.rs b/crates/jacquard-api/src/sh_weaver/notebook/chapter.rs index e34b4322..2049e263 100644 --- a/crates/jacquard-api/src/sh_weaver/notebook/chapter.rs +++ b/crates/jacquard-api/src/sh_weaver/notebook/chapter.rs @@ -34,6 +34,44 @@ pub struct Chapter<'a> { pub title: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ChapterGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Chapter<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ChapterRecord; +impl jacquard_common::xrpc::XrpcResp for ChapterRecord { + const NSID: &'static str = "sh.weaver.notebook.chapter"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ChapterGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Chapter<'_> { const NSID: &'static str = "sh.weaver.notebook.chapter"; + type Record = ChapterRecord; +} + +impl From> for Chapter<'static> { + fn from(output: ChapterGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/notebook/entry.rs b/crates/jacquard-api/src/sh_weaver/notebook/entry.rs index 016b1f76..0db8b346 100644 --- a/crates/jacquard-api/src/sh_weaver/notebook/entry.rs +++ b/crates/jacquard-api/src/sh_weaver/notebook/entry.rs @@ -34,6 +34,44 @@ pub struct Entry<'a> { pub title: crate::sh_weaver::notebook::Title<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct EntryGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Entry<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct EntryRecord; +impl jacquard_common::xrpc::XrpcResp for EntryRecord { + const NSID: &'static str = "sh.weaver.notebook.entry"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = EntryGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Entry<'_> { const NSID: &'static str = "sh.weaver.notebook.entry"; + type Record = EntryRecord; +} + +impl From> for Entry<'static> { + fn from(output: EntryGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/sh_weaver/publish/blob.rs b/crates/jacquard-api/src/sh_weaver/publish/blob.rs index c2192d06..2b93d9db 100644 --- a/crates/jacquard-api/src/sh_weaver/publish/blob.rs +++ b/crates/jacquard-api/src/sh_weaver/publish/blob.rs @@ -23,6 +23,44 @@ pub struct Blob<'a> { pub upload: jacquard_common::types::blob::Blob<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct BlobGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Blob<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct BlobRecord; +impl jacquard_common::xrpc::XrpcResp for BlobRecord { + const NSID: &'static str = "sh.weaver.publish.blob"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = BlobGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Blob<'_> { const NSID: &'static str = "sh.weaver.publish.blob"; + type Record = BlobRecord; +} + +impl From> for Blob<'static> { + fn from(output: BlobGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_clippr/actor/profile.rs b/crates/jacquard-api/src/social_clippr/actor/profile.rs index 3163a9e2..13cad70d 100644 --- a/crates/jacquard-api/src/social_clippr/actor/profile.rs +++ b/crates/jacquard-api/src/social_clippr/actor/profile.rs @@ -33,6 +33,44 @@ pub struct Profile<'a> { pub display_name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "social.clippr.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "social.clippr.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_clippr/feed/clip.rs b/crates/jacquard-api/src/social_clippr/feed/clip.rs index cc5d7bda..8966e8ea 100644 --- a/crates/jacquard-api/src/social_clippr/feed/clip.rs +++ b/crates/jacquard-api/src/social_clippr/feed/clip.rs @@ -49,6 +49,44 @@ pub struct Clip<'a> { pub url: jacquard_common::types::string::Uri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ClipGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Clip<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ClipRecord; +impl jacquard_common::xrpc::XrpcResp for ClipRecord { + const NSID: &'static str = "social.clippr.feed.clip"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ClipGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Clip<'_> { const NSID: &'static str = "social.clippr.feed.clip"; + type Record = ClipRecord; +} + +impl From> for Clip<'static> { + fn from(output: ClipGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_clippr/feed/tag.rs b/crates/jacquard-api/src/social_clippr/feed/tag.rs index 2f146670..26e004c5 100644 --- a/crates/jacquard-api/src/social_clippr/feed/tag.rs +++ b/crates/jacquard-api/src/social_clippr/feed/tag.rs @@ -33,6 +33,44 @@ pub struct Tag<'a> { pub name: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct TagGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Tag<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct TagRecord; +impl jacquard_common::xrpc::XrpcResp for TagRecord { + const NSID: &'static str = "social.clippr.feed.tag"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = TagGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Tag<'_> { const NSID: &'static str = "social.clippr.feed.tag"; + type Record = TagRecord; +} + +impl From> for Tag<'static> { + fn from(output: TagGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_grain/actor/profile.rs b/crates/jacquard-api/src/social_grain/actor/profile.rs index 74b6f6ba..1c089152 100644 --- a/crates/jacquard-api/src/social_grain/actor/profile.rs +++ b/crates/jacquard-api/src/social_grain/actor/profile.rs @@ -33,6 +33,44 @@ pub struct Profile<'a> { pub display_name: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "social.grain.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "social.grain.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_grain/favorite.rs b/crates/jacquard-api/src/social_grain/favorite.rs index bfa9e277..15da363e 100644 --- a/crates/jacquard-api/src/social_grain/favorite.rs +++ b/crates/jacquard-api/src/social_grain/favorite.rs @@ -22,6 +22,44 @@ pub struct Favorite<'a> { pub subject: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct FavoriteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Favorite<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct FavoriteRecord; +impl jacquard_common::xrpc::XrpcResp for FavoriteRecord { + const NSID: &'static str = "social.grain.favorite"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = FavoriteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Favorite<'_> { const NSID: &'static str = "social.grain.favorite"; + type Record = FavoriteRecord; +} + +impl From> for Favorite<'static> { + fn from(output: FavoriteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_grain/gallery.rs b/crates/jacquard-api/src/social_grain/gallery.rs index 36bdf683..c4525a94 100644 --- a/crates/jacquard-api/src/social_grain/gallery.rs +++ b/crates/jacquard-api/src/social_grain/gallery.rs @@ -31,8 +31,46 @@ pub struct Gallery<'a> { pub title: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct GalleryGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Gallery<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct GalleryRecord; +impl jacquard_common::xrpc::XrpcResp for GalleryRecord { + const NSID: &'static str = "social.grain.gallery"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = GalleryGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Gallery<'_> { const NSID: &'static str = "social.grain.gallery"; + type Record = GalleryRecord; +} + +impl From> for Gallery<'static> { + fn from(output: GalleryGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/social_grain/gallery/item.rs b/crates/jacquard-api/src/social_grain/gallery/item.rs index 6b2f61d3..5da491e0 100644 --- a/crates/jacquard-api/src/social_grain/gallery/item.rs +++ b/crates/jacquard-api/src/social_grain/gallery/item.rs @@ -26,6 +26,44 @@ pub struct Item<'a> { pub position: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ItemGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Item<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ItemRecord; +impl jacquard_common::xrpc::XrpcResp for ItemRecord { + const NSID: &'static str = "social.grain.gallery.item"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ItemGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Item<'_> { const NSID: &'static str = "social.grain.gallery.item"; + type Record = ItemRecord; +} + +impl From> for Item<'static> { + fn from(output: ItemGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_grain/photo.rs b/crates/jacquard-api/src/social_grain/photo.rs index 1507be6a..d73f2641 100644 --- a/crates/jacquard-api/src/social_grain/photo.rs +++ b/crates/jacquard-api/src/social_grain/photo.rs @@ -31,8 +31,46 @@ pub struct Photo<'a> { pub photo: jacquard_common::types::blob::Blob<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PhotoGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Photo<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PhotoRecord; +impl jacquard_common::xrpc::XrpcResp for PhotoRecord { + const NSID: &'static str = "social.grain.photo"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PhotoGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Photo<'_> { const NSID: &'static str = "social.grain.photo"; + type Record = PhotoRecord; +} + +impl From> for Photo<'static> { + fn from(output: PhotoGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/social_grain/photo/exif.rs b/crates/jacquard-api/src/social_grain/photo/exif.rs index d743da79..c543a639 100644 --- a/crates/jacquard-api/src/social_grain/photo/exif.rs +++ b/crates/jacquard-api/src/social_grain/photo/exif.rs @@ -50,6 +50,44 @@ pub struct Exif<'a> { pub photo: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ExifGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Exif<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ExifRecord; +impl jacquard_common::xrpc::XrpcResp for ExifRecord { + const NSID: &'static str = "social.grain.photo.exif"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ExifGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Exif<'_> { const NSID: &'static str = "social.grain.photo.exif"; + type Record = ExifRecord; +} + +impl From> for Exif<'static> { + fn from(output: ExifGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_pmsky/proposal.rs b/crates/jacquard-api/src/social_pmsky/proposal.rs index 7324b929..38e582ef 100644 --- a/crates/jacquard-api/src/social_pmsky/proposal.rs +++ b/crates/jacquard-api/src/social_pmsky/proposal.rs @@ -55,6 +55,44 @@ pub struct Proposal<'a> { pub ver: std::option::Option, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProposalGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Proposal<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProposalRecord; +impl jacquard_common::xrpc::XrpcResp for ProposalRecord { + const NSID: &'static str = "social.pmsky.proposal"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProposalGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Proposal<'_> { const NSID: &'static str = "social.pmsky.proposal"; + type Record = ProposalRecord; +} + +impl From> for Proposal<'static> { + fn from(output: ProposalGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_pmsky/vote.rs b/crates/jacquard-api/src/social_pmsky/vote.rs index 7f2aeacb..a9e99e02 100644 --- a/crates/jacquard-api/src/social_pmsky/vote.rs +++ b/crates/jacquard-api/src/social_pmsky/vote.rs @@ -44,6 +44,44 @@ pub struct Vote<'a> { pub val: i64, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct VoteGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Vote<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct VoteRecord; +impl jacquard_common::xrpc::XrpcResp for VoteRecord { + const NSID: &'static str = "social.pmsky.vote"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = VoteGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Vote<'_> { const NSID: &'static str = "social.pmsky.vote"; + type Record = VoteRecord; +} + +impl From> for Vote<'static> { + fn from(output: VoteGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_psky/actor/profile.rs b/crates/jacquard-api/src/social_psky/actor/profile.rs index 7f8c2626..e7d974b6 100644 --- a/crates/jacquard-api/src/social_psky/actor/profile.rs +++ b/crates/jacquard-api/src/social_psky/actor/profile.rs @@ -23,6 +23,44 @@ pub struct Profile<'a> { pub nickname: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ProfileGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Profile<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ProfileRecord; +impl jacquard_common::xrpc::XrpcResp for ProfileRecord { + const NSID: &'static str = "social.psky.actor.profile"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ProfileGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Profile<'_> { const NSID: &'static str = "social.psky.actor.profile"; + type Record = ProfileRecord; +} + +impl From> for Profile<'static> { + fn from(output: ProfileGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_psky/chat/message.rs b/crates/jacquard-api/src/social_psky/chat/message.rs index 62660c01..b16d7290 100644 --- a/crates/jacquard-api/src/social_psky/chat/message.rs +++ b/crates/jacquard-api/src/social_psky/chat/message.rs @@ -32,6 +32,44 @@ pub struct Message<'a> { pub room: jacquard_common::types::string::AtUri<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct MessageGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Message<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct MessageRecord; +impl jacquard_common::xrpc::XrpcResp for MessageRecord { + const NSID: &'static str = "social.psky.chat.message"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = MessageGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Message<'_> { const NSID: &'static str = "social.psky.chat.message"; + type Record = MessageRecord; +} + +impl From> for Message<'static> { + fn from(output: MessageGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/social_psky/chat/room.rs b/crates/jacquard-api/src/social_psky/chat/room.rs index 743f2c52..0531de00 100644 --- a/crates/jacquard-api/src/social_psky/chat/room.rs +++ b/crates/jacquard-api/src/social_psky/chat/room.rs @@ -39,8 +39,46 @@ pub struct Room<'a> { pub topic: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct RoomGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Room<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct RoomRecord; +impl jacquard_common::xrpc::XrpcResp for RoomRecord { + const NSID: &'static str = "social.psky.chat.room"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = RoomGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Room<'_> { const NSID: &'static str = "social.psky.chat.room"; + type Record = RoomRecord; +} + +impl From> for Room<'static> { + fn from(output: RoomGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/tools_smokesignal/blahg/content/post.rs b/crates/jacquard-api/src/tools_smokesignal/blahg/content/post.rs index 8c89597b..0ddac6d6 100644 --- a/crates/jacquard-api/src/tools_smokesignal/blahg/content/post.rs +++ b/crates/jacquard-api/src/tools_smokesignal/blahg/content/post.rs @@ -57,6 +57,44 @@ pub struct Post<'a> { pub title: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "tools.smokesignal.blahg.content.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "tools.smokesignal.blahg.content.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/uk_ewancroft/now.rs b/crates/jacquard-api/src/uk_ewancroft/now.rs index 5d54dc85..2936df03 100644 --- a/crates/jacquard-api/src/uk_ewancroft/now.rs +++ b/crates/jacquard-api/src/uk_ewancroft/now.rs @@ -24,6 +24,44 @@ pub struct Now<'a> { pub text: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct NowGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Now<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct NowRecord; +impl jacquard_common::xrpc::XrpcResp for NowRecord { + const NSID: &'static str = "uk.ewancroft.now"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = NowGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Now<'_> { const NSID: &'static str = "uk.ewancroft.now"; + type Record = NowRecord; +} + +impl From> for Now<'static> { + fn from(output: NowGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/uk_skyblur/post.rs b/crates/jacquard-api/src/uk_skyblur/post.rs index 9782bed0..4ee3bb7b 100644 --- a/crates/jacquard-api/src/uk_skyblur/post.rs +++ b/crates/jacquard-api/src/uk_skyblur/post.rs @@ -42,6 +42,44 @@ pub struct Post<'a> { pub visibility: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "uk.skyblur.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "uk.skyblur.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/uk_skyblur/preference.rs b/crates/jacquard-api/src/uk_skyblur/preference.rs index 4d579977..df610f39 100644 --- a/crates/jacquard-api/src/uk_skyblur/preference.rs +++ b/crates/jacquard-api/src/uk_skyblur/preference.rs @@ -22,8 +22,46 @@ pub struct Preference<'a> { pub my_page: crate::uk_skyblur::preference::MyPage<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PreferenceGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Preference<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PreferenceRecord; +impl jacquard_common::xrpc::XrpcResp for PreferenceRecord { + const NSID: &'static str = "uk.skyblur.preference"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PreferenceGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Preference<'_> { const NSID: &'static str = "uk.skyblur.preference"; + type Record = PreferenceRecord; +} + +impl From> for Preference<'static> { + fn from(output: PreferenceGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } #[jacquard_derive::lexicon] diff --git a/crates/jacquard-api/src/us_polhem/blog/content.rs b/crates/jacquard-api/src/us_polhem/blog/content.rs index df5e793b..f2057f4b 100644 --- a/crates/jacquard-api/src/us_polhem/blog/content.rs +++ b/crates/jacquard-api/src/us_polhem/blog/content.rs @@ -28,6 +28,44 @@ pub struct Content<'a> { pub slug: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct ContentGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Content<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct ContentRecord; +impl jacquard_common::xrpc::XrpcResp for ContentRecord { + const NSID: &'static str = "us.polhem.blog.content"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ContentGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Content<'_> { const NSID: &'static str = "us.polhem.blog.content"; + type Record = ContentRecord; +} + +impl From> for Content<'static> { + fn from(output: ContentGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/us_polhem/blog/post.rs b/crates/jacquard-api/src/us_polhem/blog/post.rs index 740db404..5d4c0153 100644 --- a/crates/jacquard-api/src/us_polhem/blog/post.rs +++ b/crates/jacquard-api/src/us_polhem/blog/post.rs @@ -43,6 +43,44 @@ pub struct Post<'a> { pub visibility: std::option::Option>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct PostGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Post<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct PostRecord; +impl jacquard_common::xrpc::XrpcResp for PostRecord { + const NSID: &'static str = "us.polhem.blog.post"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = PostGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Post<'_> { const NSID: &'static str = "us.polhem.blog.post"; + type Record = PostRecord; +} + +impl From> for Post<'static> { + fn from(output: PostGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/us_polhem/blog/tag.rs b/crates/jacquard-api/src/us_polhem/blog/tag.rs index 9b09b1f4..546ac7a0 100644 --- a/crates/jacquard-api/src/us_polhem/blog/tag.rs +++ b/crates/jacquard-api/src/us_polhem/blog/tag.rs @@ -27,6 +27,44 @@ pub struct Tag<'a> { pub slug: jacquard_common::CowStr<'a>, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct TagGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Tag<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct TagRecord; +impl jacquard_common::xrpc::XrpcResp for TagRecord { + const NSID: &'static str = "us.polhem.blog.tag"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = TagGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Tag<'_> { const NSID: &'static str = "us.polhem.blog.tag"; + type Record = TagRecord; +} + +impl From> for Tag<'static> { + fn from(output: TagGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-api/src/win_tomo_x/pushat/allow.rs b/crates/jacquard-api/src/win_tomo_x/pushat/allow.rs index ac68fdc7..3fdd3b64 100644 --- a/crates/jacquard-api/src/win_tomo_x/pushat/allow.rs +++ b/crates/jacquard-api/src/win_tomo_x/pushat/allow.rs @@ -24,6 +24,44 @@ pub struct Allow<'a> { pub created_at: jacquard_common::types::string::Datetime, } +/// Typed wrapper for GetRecord response with this collection's record type. +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + Clone, + PartialEq, + Eq, + jacquard_derive::IntoStatic +)] +#[serde(rename_all = "camelCase")] +pub struct AllowGetRecordOutput<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: Allow<'a>, +} + +/// Marker type for deserializing records from this collection. +pub struct AllowRecord; +impl jacquard_common::xrpc::XrpcResp for AllowRecord { + const NSID: &'static str = "win.tomo-x.pushat.allow"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = AllowGetRecordOutput<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; +} + impl jacquard_common::types::collection::Collection for Allow<'_> { const NSID: &'static str = "win.tomo-x.pushat.allow"; + type Record = AllowRecord; +} + +impl From> for Allow<'static> { + fn from(output: AllowGetRecordOutput<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } } \ No newline at end of file diff --git a/crates/jacquard-axum/Cargo.toml b/crates/jacquard-axum/Cargo.toml index 968c546a..7e5fe8a3 100644 --- a/crates/jacquard-axum/Cargo.toml +++ b/crates/jacquard-axum/Cargo.toml @@ -15,10 +15,10 @@ description = "Axum server helpers for Jacquard" [lib] path = "src/lib.rs" -[[bin]] -name = "jacquard_axum" -path = "src/main.rs" - +[[example]] +name = "axum_server" +path = "../../examples/axum_server.rs" +required-features = ["jacquard/fancy"] [dependencies] axum = "0.8.6" diff --git a/crates/jacquard-axum/src/lib.rs b/crates/jacquard-axum/src/lib.rs index 2e026a02..d7afe6a7 100644 --- a/crates/jacquard-axum/src/lib.rs +++ b/crates/jacquard-axum/src/lib.rs @@ -1,4 +1,52 @@ +//! # Axum helpers for jacquard XRPC server implementations +//! +//! ## Usage +//! +//! ```no_run +//! use axum::{Router, routing::get, http::StatusCode, response::IntoResponse, Json}; +//! use jacquard_axum::{ ExtractXrpc, IntoRouter }; +//! use std::collections::BTreeMap; +//! use miette::{IntoDiagnostic, Result}; +//! use jacquard::api::com_atproto::identity::resolve_handle::{ResolveHandle, ResolveHandleRequest, ResolveHandleOutput}; +//! use jacquard_common::types::string::Did; +//! +//! async fn handle_resolve( +//! ExtractXrpc(req): ExtractXrpc +//! ) -> Result>, StatusCode> { +//! // req is ResolveHandle<'static>, ready to use +//! let handle = req.handle; +//! // ... resolve logic +//! # let output = ResolveHandleOutput { did: Did::new_static("did:plc:test").unwrap(), extra_data: BTreeMap::new() }; +//! Ok(Json(output)) +//! } +//! +//! #[tokio::main] +//! async fn main() -> Result<()> { +//! let app = Router::new() +//! .route("/", axum::routing::get(|| async { "hello world!" })) +//! .merge(ResolveHandleRequest::into_router(handle_resolve)); +//! +//! let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") +//! .await +//! .into_diagnostic()?; +//! axum::serve(listener, app).await.unwrap(); +//! Ok(()) +//! } +//! ``` +//! +//! +//! The extractor uses the [`XrpcEndpoint`] trait to determine request type: +//! - **Query**: Deserializes from query string parameters +//! - **Procedure**: Deserializes from request body (supports custom encodings via `decode_body`) +//! +//! Deserialization errors return a 400 Bad Request with a JSON error body matching +//! the XRPC error format. +//! +//! The extractor deserializes to borrowed types first, then converts to `'static` via +//! [`IntoStatic`], avoiding the DeserializeOwned requirement of the Json axum extractor and similar. + use axum::{ + Router, body::Bytes, extract::{FromRequest, Request}, http::StatusCode, @@ -10,6 +58,11 @@ use jacquard::{ }; use serde_json::json; +/// Axum extractor for XRPC requests +/// +/// Deserializes incoming requests based on the endpoint's method type (Query or Procedure) +/// and returns the owned (`'static`) request type ready for handler logic. + pub struct ExtractXrpc(pub E::Request<'static>); impl FromRequest for ExtractXrpc @@ -86,3 +139,34 @@ pub enum XrpcRequestError { #[error("UTF-8 decode error: {0}")] Utf8DecodeError(std::string::FromUtf8Error), } + +/// Conversion trait to turn an XrpcEndpoint and a handler into an axum Router +pub trait IntoRouter { + fn into_router(handler: U) -> Router + where + T: 'static, + S: Clone + Send + Sync + 'static, + U: axum::handler::Handler; +} + +impl IntoRouter for X +where + X: XrpcEndpoint, +{ + /// Creates an axum router that will invoke `handler` in response to xrpc + /// request `X`. + fn into_router(handler: U) -> Router + where + T: 'static, + S: Clone + Send + Sync + 'static, + U: axum::handler::Handler, + { + Router::new().route( + X::PATH, + (match X::METHOD { + XrpcMethod::Query => axum::routing::get, + XrpcMethod::Procedure(_) => axum::routing::post, + })(handler), + ) + } +} diff --git a/crates/jacquard-axum/src/main.rs b/crates/jacquard-axum/src/main.rs deleted file mode 100644 index c34e8a72..00000000 --- a/crates/jacquard-axum/src/main.rs +++ /dev/null @@ -1,73 +0,0 @@ -use axum::{Router, response::IntoResponse}; -use jacquard::{ - IntoStatic, - api::com_atproto::identity::resolve_did::{ResolveDid, ResolveDidOutput, ResolveDidRequest}, - identity::resolver::IdentityResolver, - types::value::Data, - xrpc::XrpcEndpoint, -}; -use jacquard_axum::ExtractXrpc; -use jacquard_common::xrpc::XrpcMethod; -use miette::{IntoDiagnostic, Result}; -use tracing_subscriber::EnvFilter; - -trait IntoRouter { - fn into_router(handler: U) -> Router - where - T: 'static, - S: Clone + Send + Sync + 'static, - U: axum::handler::Handler; -} - -impl IntoRouter for X -where - X: XrpcEndpoint, -{ - /// Creates an axum router that will invoke `handler` in response to xrpc - /// request `X`. - fn into_router(handler: U) -> Router - where - T: 'static, - S: Clone + Send + Sync + 'static, - U: axum::handler::Handler, - { - Router::new().route( - X::PATH, - (match X::METHOD { - XrpcMethod::Query => axum::routing::get, - XrpcMethod::Procedure(_) => axum::routing::post, - })(handler), - ) - } -} - -#[axum_macros::debug_handler] -async fn handler(ExtractXrpc(args): ExtractXrpc) -> &'static str { - "hello world!" - // let res = jacquard::identity::slingshot_resolver_default(); - // let doc = res.resolve_did_doc(&args.did).await?; - // let valid_doc = doc.parse()?; - // let doc_value = serde_json::to_value(valid_doc).unwrap(); - // Ok(ResolveDidOutput { - // did_doc: Data::from_json(&doc_value).unwrap().into_static(), - // extra_data: Default::default(), - // } - // .into()) -} - -#[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc_3339()) - .with_env_filter(EnvFilter::from_env("QDPDS_LOG")) - .init(); - let app = Router::new() - .route("/", axum::routing::get(|| async { "hello world!" })) - .merge(ResolveDidRequest::into_router(handler)) - .layer(tower_http::trace::TraceLayer::new_for_http()); - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") - .await - .into_diagnostic()?; - axum::serve(listener, app).await.unwrap(); - Ok(()) -} diff --git a/crates/jacquard-common/Cargo.toml b/crates/jacquard-common/Cargo.toml index ab90238d..2c4ac995 100644 --- a/crates/jacquard-common/Cargo.toml +++ b/crates/jacquard-common/Cargo.toml @@ -23,7 +23,6 @@ langtag = { version = "0.4.0", features = ["serde"] } miette.workspace = true multibase = "0.9.1" multihash = "0.19.3" -num-traits = "0.2.19" ouroboros = "0.18.5" rand = "0.9.2" regex = "1.11.3" diff --git a/crates/jacquard-common/src/lib.rs b/crates/jacquard-common/src/lib.rs index 3765736d..817ac6ca 100644 --- a/crates/jacquard-common/src/lib.rs +++ b/crates/jacquard-common/src/lib.rs @@ -1,6 +1,186 @@ -//! Common types for the jacquard implementation of atproto +//! # Common types for the jacquard implementation of atproto +//! +//! ## Working with Lifetimes and Zero-Copy Deserialization +//! +//! Jacquard is designed around zero-copy deserialization: types like `Post<'de>` can borrow +//! strings and other data directly from the response buffer instead of allocating owned copies. +//! This is great for performance, but it creates some interesting challenges when combined with +//! async Rust and trait bounds. +//! +//! ### The Problem: Lifetimes + Async + Traits +//! +//! The naive approach would be to put a lifetime parameter on the trait itself: +//! +//! ```ignore +//! trait XrpcRequest<'de> { +//! type Output: Deserialize<'de>; +//! // ... +//! } +//! ``` +//! +//! This looks reasonable until you try to use it in a generic context. If you have a function +//! that works with *any* lifetime, you need a Higher-Ranked Trait Bound (HRTB): +//! +//! ```ignore +//! fn foo(response: &[u8]) +//! where +//! R: for<'any> XrpcRequest<'any> +//! { +//! // deserialize from response... +//! } +//! ``` +//! +//! The `for<'any>` bound says "this type must implement `XrpcRequest` for *every possible lifetime*", +//! which is effectively the same as requiring `DeserializeOwned`. You've just thrown away your +//! zero-copy optimization, and this also won't work on most of the types in jacquard. The vast +//! majority of them have either a custom Deserialize implementation which will borrow if it +//! can, a #[serde(borrow)] attribute on one or more fields, or an equivalent lifetime bound +//! attribute, associated with the Deserialize derive macro. +//! +//! It gets worse with async. If you want to return borrowed data from an async method, where does +//! the lifetime come from? The response buffer needs to outlive the borrow, but the buffer is +//! consumed by the HTTP call. You end up with "cannot infer appropriate lifetime" errors or even +//! more confusing errors because the compiler can't prove the buffer will stay alive. You *could* +//! do some lifetime laundering with `unsafe`, but you don't actually *need* to tell rustc to "trust +//! me, bro", you can, with some cleverness, explain this to the compiler in a way that it can +//! reason about perfectly well. +//! +//! ### Explaining where the buffer goes to `rustc`: GATs + Method-Level Lifetimes +//! +//! The fix is to use Generic Associated Types (GATs) on the trait's associated types, while keeping +//! the trait itself lifetime-free: +//! +//! ```ignore +//! trait XrpcResp { +//! const NSID: &'static str; +//! +//! // GATs: lifetime is on the associated type, not the trait +//! type Output<'de>: Deserialize<'de> + IntoStatic; +//! type Err<'de>: Deserialize<'de> + IntoStatic; +//! } +//! ``` +//! +//! Now you can write trait bounds without HRTBs: +//! +//! ```ignore +//! fn foo(response: &[u8]) { +//! // Compiler can pick a concrete lifetime for R::Output<'_> +//! } +//! ``` +//! +//! Methods that need lifetimes use method-level generic parameters: +//! +//! ```ignore +//! // This is part of a trait from jacquard itself, used to genericize updates to the Bluesky +//! // preferences union, so that if you implement a similar lexicon type in your AppView or App +//! // Server API, you don't have to special-case it. +//! +//! trait VecUpdate { +//! type GetRequest<'de>: XrpcRequest<'de>; // GAT +//! type PutRequest<'de>: XrpcRequest<'de>; // GAT +//! +//! // Method-level lifetime, not trait-level +//! fn extract_vec<'s>( +//! output: as XrpcRequest<'s>>::Output<'s> +//! ) -> Vec; +//! } +//! ``` +//! +//! The compiler can monomorphize for concrete lifetimes instead of trying to prove bounds hold +//! for *all* lifetimes at once. +//! +//! ### Handling Async with `Response` +//! +//! For the async problem, we use a wrapper type that owns the response buffer: +//! +//! ```ignore +//! pub struct Response { +//! buffer: Bytes, // Refcounted, cheap to clone +//! status: StatusCode, +//! _marker: PhantomData, +//! } +//! ``` +//! +//! This lets async methods return a `Response` that owns its buffer, then the *caller* decides +//! the lifetime strategy: +//! +//! ```ignore +//! // Zero-copy: borrow from the owned buffer +//! let output: R::Output<'_> = response.parse()?; +//! +//! // Owned: convert to 'static via IntoStatic +//! let output: R::Output<'static> = response.into_output()?; +//! ``` +//! +//! The async method doesn't need to know or care about lifetimes - it just returns the `Response`. +//! The caller gets full control over whether to use borrowed or owned data. It can even decide +//! after the fact that it doesn't want to parse out the API response type that it asked for. Instead +//! it can call `.parse_data()` or `.parse_raw()` on the response to get loosely typed, validated +//! data or minimally typed maximally accepting data values out. +//! +//! ### Example: XRPC Traits in Practice +//! +//! Here's how the pattern works with the XRPC layer: +//! +//! ```ignore +//! // XrpcResp uses GATs, not trait-level lifetime +//! trait XrpcResp { +//! const NSID: &'static str; +//! type Output<'de>: Deserialize<'de> + IntoStatic; +//! type Err<'de>: Deserialize<'de> + IntoStatic; +//! } +//! +//! // Response owns the buffer (Bytes is refcounted) +//! pub struct Response { +//! buffer: Bytes, +//! status: StatusCode, +//! _marker: PhantomData, +//! } +//! +//! impl Response { +//! // Borrow from owned buffer +//! pub fn parse(&self) -> XrpcResult> { +//! serde_json::from_slice(&self.buffer) +//! } +//! +//! // Convert to fully owned +//! pub fn into_output(self) -> XrpcResult> { +//! let borrowed = self.parse()?; +//! Ok(borrowed.into_static()) +//! } +//! } +//! +//! // Async method returns Response, caller chooses strategy +//! async fn send_xrpc(&self, req: Req) -> Result> +//! where +//! Req: XrpcRequest<'_> +//! { +//! // Do HTTP call, get Bytes buffer +//! // Return Response wrapping that buffer +//! // No lifetime issues - Response owns the buffer +//! } +//! +//! // Usage: +//! let response = send_xrpc(request).await?; +//! +//! // Zero-copy: borrow from response buffer +//! let output = response.parse()?; // Output<'_> borrows from response +//! +//! // Or owned: convert to 'static +//! let output = response.into_output()?; // Output<'static> is fully owned +//! ``` +//! +//! When you see types like `Response` or methods with lifetime parameters, +//! this is the pattern at work. It looks a bit funky, but it's solving a specific problem +//! in a way that doesn't require unsafe code or much actual work from you, if you're using it. +//! It's also not too bad to write, once you're aware of the pattern and why it works. If you run +//! into a lifetime/borrowing inference issue in jacquard, please contact the crate author. She'd +//! be happy to debug, and if it's using a method from one of the jacquard crates and seems like +//! it *should* just work, that is a bug in jacquard, and you should [file an issue](https://tangled.org/@nonbinary.computer/jacquard/). #![warn(missing_docs)] +pub use bytes; +pub use chrono; pub use cowstr::CowStr; pub use into_static::IntoStatic; pub use smol_str; @@ -21,7 +201,7 @@ pub mod macros; pub mod session; /// Baseline fundamental AT Protocol data types. pub mod types; -/// XRPC protocol types and traits +// XRPC protocol types and traits pub mod xrpc; /// Authorization token types for XRPC requests. diff --git a/crates/jacquard-common/src/types/collection.rs b/crates/jacquard-common/src/types/collection.rs index 63df3946..a8fcc3e6 100644 --- a/crates/jacquard-common/src/types/collection.rs +++ b/crates/jacquard-common/src/types/collection.rs @@ -1,12 +1,15 @@ use core::fmt; -use serde::Serialize; +use serde::{Deserialize, Serialize}; +use crate::IntoStatic; +use crate::types::value::Data; use crate::types::{ aturi::RepoPath, nsid::Nsid, recordkey::{RecordKey, RecordKeyType, Rkey}, }; +use crate::xrpc::XrpcResp; /// Trait for a collection of records that can be stored in a repository. /// @@ -17,6 +20,10 @@ pub trait Collection: fmt::Debug + Serialize { /// The NSID for the Lexicon that defines the schema of records in this collection. const NSID: &'static str; + /// A marker type implementing [`XrpcResp`] that allows typed deserialization of records + /// from this collection. Used by [`Agent::get_record`] to return properly typed responses. + type Record: XrpcResp; + /// Returns the [`Nsid`] for the Lexicon that defines the schema of records in this /// collection. /// @@ -49,3 +56,33 @@ pub trait Collection: fmt::Debug + Serialize { } } } + +/// Generic error type for record retrieval operations. +/// +/// Used by generated collection marker types as their error type. +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, thiserror::Error, miette::Diagnostic, +)] +#[serde(tag = "error", content = "message")] +pub enum RecordError<'a> { + /// The requested record was not found + #[error("RecordNotFound")] + #[serde(rename = "RecordNotFound")] + RecordNotFound(Option), + /// An unknown error occurred + #[error("Unknown")] + #[serde(rename = "Unknown")] + #[serde(borrow)] + Unknown(Data<'a>), +} + +impl IntoStatic for RecordError<'_> { + type Output = RecordError<'static>; + + fn into_static(self) -> Self::Output { + match self { + RecordError::RecordNotFound(msg) => RecordError::RecordNotFound(msg), + RecordError::Unknown(data) => RecordError::Unknown(data.into_static()), + } + } +} diff --git a/crates/jacquard-common/src/types/did_doc.rs b/crates/jacquard-common/src/types/did_doc.rs index 6c7b6437..f95f26a4 100644 --- a/crates/jacquard-common/src/types/did_doc.rs +++ b/crates/jacquard-common/src/types/did_doc.rs @@ -15,17 +15,29 @@ use url::Url; /// pattern as lexicon structs. /// /// Example -/// ```ignore +/// ``` /// use jacquard_common::types::did_doc::DidDocument; -/// use serde_json::json; -/// let doc: DidDocument<'_> = serde_json::from_value(json!({ +/// +/// # fn example() -> Result<(), Box> { +/// let json = r##"{ /// "id": "did:plc:alice", /// "alsoKnownAs": ["at://alice.example"], -/// "service": [{"id":"#pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.example"}], -/// "verificationMethod":[{"id":"#k","type":"Multikey","publicKeyMultibase":"z6Mki..."}] -/// })).unwrap(); +/// "service": [{ +/// "id": "#pds", +/// "type": "AtprotoPersonalDataServer", +/// "serviceEndpoint": "https://pds.example" +/// }], +/// "verificationMethod": [{ +/// "id": "#k", +/// "type": "Multikey", +/// "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK" +/// }] +/// }"##; +/// let doc: DidDocument<'_> = serde_json::from_str(json)?; /// assert_eq!(doc.id.as_str(), "did:plc:alice"); /// assert!(doc.pds_endpoint().is_some()); +/// # Ok(()) +/// # } /// ``` #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)] #[builder(start_fn = new)] diff --git a/crates/jacquard-common/src/types/value.rs b/crates/jacquard-common/src/types/value.rs index 8f21e19e..d6923ce3 100644 --- a/crates/jacquard-common/src/types/value.rs +++ b/crates/jacquard-common/src/types/value.rs @@ -358,10 +358,10 @@ pub enum RawData<'s> { /// similar to `serde_json::from_value()`. /// /// # Example -/// ```ignore -/// use jacquard_common::types::value::{Data, from_data}; -/// use serde::Deserialize; -/// +/// ``` +/// # use jacquard_common::types::value::{Data, from_data}; +/// # use serde::Deserialize; +/// # /// #[derive(Deserialize)] /// struct Post<'a> { /// #[serde(borrow)] @@ -370,8 +370,12 @@ pub enum RawData<'s> { /// author: &'a str, /// } /// -/// let data: Data = /* ... */; +/// # fn example() -> Result<(), Box> { +/// # let json = serde_json::json!({"text": "hello", "author": "alice"}); +/// # let data = Data::from_json(&json)?; /// let post: Post = from_data(&data)?; +/// # Ok(()) +/// # } /// ``` pub fn from_data<'de, T>(data: &'de Data<'de>) -> Result where @@ -385,20 +389,22 @@ where /// Allows extracting strongly-typed structures from untyped `RawData` values. /// /// # Example -/// ```ignore -/// use jacquard_common::types::value::{RawData, from_raw_data}; -/// use serde::Deserialize; -/// -/// #[derive(Deserialize)] -/// struct Post<'a> { -/// #[serde(borrow)] -/// text: &'a str, -/// #[serde(borrow)] -/// author: &'a str, +/// ``` +/// # use jacquard_common::types::value::{RawData, from_raw_data, to_raw_data}; +/// # use serde::{Serialize, Deserialize}; +/// # +/// #[derive(Serialize, Deserialize)] +/// struct Post { +/// text: String, +/// author: String, /// } /// -/// let data: RawData = /* ... */; +/// # fn example() -> Result<(), Box> { +/// # let orig = Post { text: "hello".to_string(), author: "alice".to_string() }; +/// # let data = to_raw_data(&orig)?; /// let post: Post = from_raw_data(&data)?; +/// # Ok(()) +/// # } /// ``` pub fn from_raw_data<'de, T>(data: &'de RawData<'de>) -> Result where @@ -412,18 +418,21 @@ where /// Allows converting strongly-typed structures into untyped `RawData` values. /// /// # Example -/// ```ignore -/// use jacquard_common::types::value::{RawData, to_raw_data}; -/// use serde::Serialize; -/// +/// ``` +/// # use jacquard_common::types::value::{RawData, to_raw_data}; +/// # use serde::Serialize; +/// # /// #[derive(Serialize)] /// struct Post { /// text: String, /// likes: i64, /// } /// +/// # fn example() -> Result<(), Box> { /// let post = Post { text: "hello".to_string(), likes: 42 }; /// let data: RawData = to_raw_data(&post)?; +/// # Ok(()) +/// # } /// ``` pub fn to_raw_data(value: &T) -> Result, serde_impl::RawDataSerializerError> where @@ -437,21 +446,24 @@ where /// Combines `to_raw_data()` and validation/type inference in one step. /// /// # Example -/// ```ignore -/// use jacquard_common::types::value::{Data, to_data}; -/// use serde::Serialize; -/// +/// ``` +/// # use jacquard_common::types::value::{Data, to_data}; +/// # use serde::Serialize; +/// # /// #[derive(Serialize)] /// struct Post { /// text: String, /// did: String, // Will be inferred as Did if valid /// } /// +/// # fn example() -> Result<(), Box> { /// let post = Post { /// text: "hello".to_string(), /// did: "did:plc:abc123".to_string() /// }; /// let data: Data = to_data(&post)?; +/// # Ok(()) +/// # } /// ``` pub fn to_data(value: &T) -> Result, convert::ConversionError> where diff --git a/crates/jacquard-common/src/xrpc.rs b/crates/jacquard-common/src/xrpc.rs index 4c58ee39..31a58129 100644 --- a/crates/jacquard-common/src/xrpc.rs +++ b/crates/jacquard-common/src/xrpc.rs @@ -1,4 +1,4 @@ -//! Stateless XRPC utilities and request/response mapping +//! # Stateless XRPC utilities and request/response mapping //! //! Mapping overview: //! - Success (2xx): parse body into the endpoint's typed output. @@ -9,7 +9,6 @@ //! can inspect `error="invalid_token"` or `error="use_dpop_nonce"` and refresh/retry. //! If the header is absent, parse the body and map auth errors to //! `AuthError::TokenExpired`/`InvalidToken`. -//! use bytes::Bytes; use http::{ HeaderName, HeaderValue, Request, StatusCode, @@ -135,7 +134,7 @@ pub trait XrpcResp { /// /// It is implemented by the code generation on a marker struct, like the client-side [XrpcResp] trait. pub trait XrpcEndpoint { - /// Fully-qualified path ('/xrpc/[nsid]') where this endpoint should live on the server + /// Fully-qualified path ('/xrpc/\[nsid\]') where this endpoint should live on the server const PATH: &'static str; /// XRPC method (query/GET or procedure/POST) const METHOD: XrpcMethod; @@ -195,29 +194,17 @@ impl IntoStatic for CallOptions<'_> { /// Extension for stateless XRPC calls on any `HttpClient`. /// /// Example -/// ```ignore -/// use jacquard::client::XrpcExt; -/// use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed; -/// use jacquard::types::ident::AtIdentifier; -/// use miette::IntoDiagnostic; +/// ```no_run +/// # #[tokio::main] +/// # async fn main() -> Result<(), Box> { +/// use jacquard_common::xrpc::XrpcExt; +/// use jacquard_common::http_client::HttpClient; /// -/// #[tokio::main] -/// async fn main() -> miette::Result<()> { -/// let http = reqwest::Client::new(); -/// let base = url::Url::parse("https://public.api.bsky.app")?; -/// let resp = http -/// .xrpc(base) -/// .send( -/// GetAuthorFeed::new() -/// .actor(AtIdentifier::new_static("pattern.atproto.systems").unwrap()) -/// .limit(5) -/// .build(), -/// ) -/// .await?; -/// let out = resp.into_output()?; -/// println!("author feed:\n{}", serde_json::to_string_pretty(&out).into_diagnostic()?); -/// Ok(()) -/// } +/// let http = reqwest::Client::new(); +/// let base = url::Url::parse("https://public.api.bsky.app")?; +/// // let resp = http.xrpc(base).send(&request).await?; +/// # Ok(()) +/// # } /// ``` pub trait XrpcExt: HttpClient { /// Start building an XRPC call for the given base URL. @@ -256,33 +243,22 @@ pub trait XrpcClient: HttpClient { /// Stateless XRPC call builder. /// /// Example (per-request overrides) -/// ```ignore -/// use jacquard::client::{XrpcExt, AuthorizationToken}; -/// use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed; -/// use jacquard::types::ident::AtIdentifier; -/// use jacquard::CowStr; -/// use miette::IntoDiagnostic; +/// ```no_run +/// # #[tokio::main] +/// # async fn main() -> Result<(), Box> { +/// use jacquard_common::xrpc::XrpcExt; +/// use jacquard_common::{AuthorizationToken, CowStr}; /// -/// #[tokio::main] -/// async fn main() -> miette::Result<()> { -/// let http = reqwest::Client::new(); -/// let base = url::Url::parse("https://public.api.bsky.app")?; -/// let resp = http -/// .xrpc(base) -/// .auth(AuthorizationToken::Bearer(CowStr::from("ACCESS_JWT"))) -/// .accept_labelers(vec![CowStr::from("did:plc:labelerid")]) -/// .header(http::header::USER_AGENT, http::HeaderValue::from_static("jacquard-example")) -/// .send( -/// GetAuthorFeed::new() -/// .actor(AtIdentifier::new_static("pattern.atproto.systems").unwrap()) -/// .limit(5) -/// .build(), -/// ) -/// .await?; -/// let out = resp.into_output()?; -/// println!("{}", serde_json::to_string_pretty(&out).into_diagnostic()?); -/// Ok(()) -/// } +/// let http = reqwest::Client::new(); +/// let base = url::Url::parse("https://public.api.bsky.app")?; +/// let call = http +/// .xrpc(base) +/// .auth(AuthorizationToken::Bearer(CowStr::from("ACCESS_JWT"))) +/// .accept_labelers(vec![CowStr::from("did:plc:labelerid")]) +/// .header(http::header::USER_AGENT, http::HeaderValue::from_static("jacquard-example")); +/// // let resp = call.send(&request).await?; +/// # Ok(()) +/// # } /// ``` pub struct XrpcCall<'a, C: HttpClient> { pub(crate) client: &'a C, @@ -686,6 +662,25 @@ where } } } + + /// Reinterpret this response as a different response type. + /// + /// This transmutes the response by keeping the same buffer and status code, + /// but changing the type-level marker. Useful for converting generic XRPC responses + /// into collection-specific typed responses. + /// + /// # Safety + /// + /// This is safe in the sense that no memory unsafety occurs, but logical correctness + /// depends on ensuring the buffer actually contains data that can deserialize to `NEW`. + /// Incorrect conversion will cause deserialization errors at runtime. + pub fn transmute(self) -> Response { + Response { + buffer: self.buffer, + status: self.status, + _marker: PhantomData, + } + } } impl Response diff --git a/crates/jacquard-derive/src/lib.rs b/crates/jacquard-derive/src/lib.rs index 7e27600b..cd42ee2b 100644 --- a/crates/jacquard-derive/src/lib.rs +++ b/crates/jacquard-derive/src/lib.rs @@ -1,3 +1,59 @@ +//! # Derive macros for jacquard lexicon types +//! +//! This crate provides attribute macros that the code generator uses to add lexicon-specific +//! behavior to generated types. You'll rarely need to use these directly unless you're writing +//! custom lexicon types by hand. However, deriving IntoStatic will likely be very useful. +//! +//! ## Macros +//! +//! ### `#[lexicon]` +//! +//! Adds an `extra_data` field to structs to capture unknown fields during deserialization. +//! This makes objects "open" - they'll accept and preserve fields not defined in the schema. +//! +//! ```ignore +//! #[lexicon] +//! struct Post<'s> { +//! text: &'s str, +//! } +//! // Expands to add: +//! // #[serde(flatten)] +//! // pub extra_data: BTreeMap> +//! ``` +//! +//! ### `#[open_union]` +//! +//! Adds an `Unknown(Data)` variant to enums to make them extensible unions. This lets +//! enums accept variants not defined in your code, storing them as loosely typed atproto `Data`. +//! +//! ```ignore +//! #[open_union] +//! enum RecordEmbed<'s> { +//! #[serde(rename = "app.bsky.embed.images")] +//! Images(Images), +//! } +//! // Expands to add: +//! // #[serde(untagged)] +//! // Unknown(Data<'s>) +//! ``` +//! +//! ### `#[derive(IntoStatic)]` +//! +//! Derives conversion from borrowed (`'a`) to owned (`'static`) types by recursively calling +//! `.into_static()` on all fields. Works with structs and enums. +//! +//! ```ignore +//! #[derive(IntoStatic)] +//! struct Post<'a> { +//! text: CowStr<'a>, +//! } +//! // Generates: +//! // impl IntoStatic for Post<'_> { +//! // type Output = Post<'static>; +//! // fn into_static(self) -> Self::Output { ... } +//! // } +//! ``` + use proc_macro::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, GenericParam, parse_macro_input}; @@ -160,17 +216,15 @@ pub fn derive_into_static(input: TokenStream) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); // Build the Output type with all lifetimes replaced by 'static - let output_generics = generics.params.iter().map(|param| { - match param { - GenericParam::Lifetime(_) => quote! { 'static }, - GenericParam::Type(ty) => { - let ident = &ty.ident; - quote! { #ident } - } - GenericParam::Const(c) => { - let ident = &c.ident; - quote! { #ident } - } + let output_generics = generics.params.iter().map(|param| match param { + GenericParam::Lifetime(_) => quote! { 'static }, + GenericParam::Type(ty) => { + let ident = &ty.ident; + quote! { #ident } + } + GenericParam::Const(c) => { + let ident = &c.ident; + quote! { #ident } } }); @@ -182,19 +236,12 @@ pub fn derive_into_static(input: TokenStream) -> TokenStream { // Generate the conversion body based on struct/enum let conversion = match &input.data { - Data::Struct(data_struct) => { - generate_struct_conversion(name, &data_struct.fields) - } - Data::Enum(data_enum) => { - generate_enum_conversion(name, data_enum) - } + Data::Struct(data_struct) => generate_struct_conversion(name, &data_struct.fields), + Data::Enum(data_enum) => generate_enum_conversion(name, data_enum), Data::Union(_) => { - return syn::Error::new_spanned( - input, - "IntoStatic cannot be derived for unions" - ) - .to_compile_error() - .into(); + return syn::Error::new_spanned(input, "IntoStatic cannot be derived for unions") + .to_compile_error() + .into(); } }; @@ -239,7 +286,10 @@ fn generate_struct_conversion(name: &syn::Ident, fields: &Fields) -> proc_macro2 } } -fn generate_enum_conversion(name: &syn::Ident, data_enum: &syn::DataEnum) -> proc_macro2::TokenStream { +fn generate_enum_conversion( + name: &syn::Ident, + data_enum: &syn::DataEnum, +) -> proc_macro2::TokenStream { let variants = data_enum.variants.iter().map(|variant| { let variant_name = &variant.ident; match &variant.fields { @@ -258,7 +308,9 @@ fn generate_enum_conversion(name: &syn::Ident, data_enum: &syn::DataEnum) -> pro } Fields::Unnamed(fields) => { let field_bindings: Vec<_> = (0..fields.unnamed.len()) - .map(|i| syn::Ident::new(&format!("field_{}", i), proc_macro2::Span::call_site())) + .map(|i| { + syn::Ident::new(&format!("field_{}", i), proc_macro2::Span::call_site()) + }) .collect(); let field_conversions = field_bindings.iter().map(|binding| { quote! { #binding.into_static() } diff --git a/crates/jacquard-identity/src/lib.rs b/crates/jacquard-identity/src/lib.rs index 5c2eb6e0..49841f61 100644 --- a/crates/jacquard-identity/src/lib.rs +++ b/crates/jacquard-identity/src/lib.rs @@ -1,15 +1,69 @@ -//! Identity resolution utilities: DID and handle resolution, DID document fetch, -//! and helpers for PDS endpoint discovery. See `identity::resolver` for details. -//! Identity resolution: handle → DID and DID → document, with smart fallbacks. +//! Identity resolution for the AT Protocol //! -//! Fallback order (default): -//! - Handle → DID: DNS TXT (if `dns` feature) → HTTPS well-known → PDS XRPC -//! `resolveHandle` (when `pds_fallback` is configured) → public API fallback → Slingshot `resolveHandle` (if configured). -//! - DID → Doc: did:web well-known → PLC/Slingshot HTTP → PDS XRPC `resolveDid` (when configured), -//! then Slingshot mini‑doc (partial) if configured. +//! Jacquard's handle-to-DID and DID-to-document resolution with configurable +//! fallback chains. //! -//! Parsing returns a `DidDocResponse` so callers can borrow from the response buffer -//! and optionally validate the document `id` against the requested DID. +//! ## Quick start +//! +//! ```no_run +//! # async fn example() -> Result<(), Box> { +//! use jacquard_identity::{PublicResolver, resolver::IdentityResolver}; +//! use jacquard_common::types::string::Handle; +//! +//! let resolver = PublicResolver::default(); +//! +//! // Resolve handle to DID +//! let did = resolver.resolve_handle(&Handle::new("alice.bsky.social")?).await?; +//! +//! // Fetch DID document +//! let doc_response = resolver.resolve_did_doc(&did).await?; +//! let doc = doc_response.parse()?; // Borrow from response buffer +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Resolution fallback order +//! +//! **Handle → DID** (configurable via [`resolver::HandleStep`]): +//! 1. DNS TXT record at `_atproto.{handle}` (if `dns` feature enabled) +//! 2. HTTPS well-known at `https://{handle}/.well-known/atproto-did` +//! 3. PDS XRPC `com.atproto.identity.resolveHandle` (if PDS configured) +//! 4. Public API fallback (`https://public.api.bsky.app`) +//! 5. Slingshot `resolveHandle` (if configured) +//! +//! **DID → Document** (configurable via [`resolver::DidStep`]): +//! 1. `did:web` HTTPS well-known +//! 2. PLC directory HTTP (for `did:plc`) +//! 3. PDS XRPC `com.atproto.identity.resolveDid` (if PDS configured) +//! 4. Slingshot mini-doc (partial document) +//! +//! ## Customization +//! +//! ``` +//! use jacquard_identity::JacquardResolver; +//! use jacquard_identity::resolver::{ResolverOptions, PlcSource}; +//! +//! let opts = ResolverOptions { +//! plc_source: PlcSource::slingshot_default(), +//! public_fallback_for_handle: true, +//! validate_doc_id: true, +//! ..Default::default() +//! }; +//! +//! let resolver = JacquardResolver::new(reqwest::Client::new(), opts); +//! #[cfg(feature = "dns")] +//! let resolver = resolver.with_system_dns(); // Enable DNS TXT resolution +//! ``` +//! +//! ## Response types +//! +//! Resolution methods return wrapper types that own the response buffer, allowing +//! zero-copy parsing: +//! +//! - [`resolver::DidDocResponse`] - Full DID document response +//! - [`MiniDocResponse`] - Slingshot mini-doc response (partial) +//! +//! Both support `.parse()` for borrowing and validation. // use crate::CowStr; // not currently needed directly here pub mod resolver; @@ -387,7 +441,6 @@ impl IdentityResolver for JacquardResolver { } PlcSource::Slingshot { base } => base.join(did.as_str())?, }; - println!("Fetching DID document from {}", url); if let Ok((buf, status)) = self.get_json_bytes(url).await { return Ok(DidDocResponse { buffer: buf, diff --git a/crates/jacquard-lexicon/src/codegen/structs.rs b/crates/jacquard-lexicon/src/codegen/structs.rs index efa5fcba..fdf12466 100644 --- a/crates/jacquard-lexicon/src/codegen/structs.rs +++ b/crates/jacquard-lexicon/src/codegen/structs.rs @@ -6,8 +6,8 @@ use heck::{ToPascalCase, ToSnakeCase}; use proc_macro2::TokenStream; use quote::quote; -use super::utils::{make_ident, value_to_variant_name}; use super::CodeGenerator; +use super::utils::{make_ident, value_to_variant_name}; /// Enum variant kind for IntoStatic generation #[derive(Debug, Clone)] @@ -52,16 +52,26 @@ impl<'c> CodeGenerator<'c> { match field_type { LexObjectProperty::Union(union) => { // Skip empty, single-variant unions unless they're self-referential - if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, &type_name, union)) { - let union_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); + if !union.refs.is_empty() + && (union.refs.len() > 1 + || self.is_self_referential_union(nsid, &type_name, union)) + { + let union_name = + self.generate_field_type_name(nsid, &type_name, field_name, ""); let refs: Vec<_> = union.refs.iter().cloned().collect(); - let union_def = - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; + let union_def = self.generate_union( + nsid, + &union_name, + &refs, + None, + union.closed, + )?; unions.push(union_def); } } LexObjectProperty::Object(nested_obj) => { - let object_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); + let object_name = + self.generate_field_type_name(nsid, &type_name, field_name, ""); let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; unions.push(obj_def); } @@ -69,9 +79,17 @@ impl<'c> CodeGenerator<'c> { if let LexArrayItem::Union(union) = &array.items { // Skip single-variant array unions if union.refs.len() > 1 { - let union_name = self.generate_field_type_name(nsid, &type_name, field_name, "Item"); + let union_name = self.generate_field_type_name( + nsid, &type_name, field_name, "Item", + ); let refs: Vec<_> = union.refs.iter().cloned().collect(); - let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; + let union_def = self.generate_union( + nsid, + &union_name, + &refs, + None, + union.closed, + )?; unions.push(union_def); } } @@ -80,17 +98,68 @@ impl<'c> CodeGenerator<'c> { } } + // Generate typed GetRecordOutput wrapper + let output_type_name = format!("{}GetRecordOutput", type_name); + let output_type_ident = + syn::Ident::new(&output_type_name, proc_macro2::Span::call_site()); + + let output_wrapper = quote! { + /// Typed wrapper for GetRecord response with this collection's record type. + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] + #[serde(rename_all = "camelCase")] + pub struct #output_type_ident<'a> { + #[serde(skip_serializing_if = "std::option::Option::is_none")] + #[serde(borrow)] + pub cid: std::option::Option>, + #[serde(borrow)] + pub uri: jacquard_common::types::string::AtUri<'a>, + #[serde(borrow)] + pub value: #ident<'a>, + } + }; + + // Generate marker struct for XrpcResp + let record_marker_name = format!("{}Record", type_name); + let record_marker_ident = + syn::Ident::new(&record_marker_name, proc_macro2::Span::call_site()); + + let record_marker = quote! { + /// Marker type for deserializing records from this collection. + pub struct #record_marker_ident; + + impl jacquard_common::xrpc::XrpcResp for #record_marker_ident { + const NSID: &'static str = #nsid; + const ENCODING: &'static str = "application/json"; + type Output<'de> = #output_type_ident<'de>; + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; + } + + + }; + let from_impl = quote! { + impl From<#output_type_ident<'_>> for #ident<'static> { + fn from(output: #output_type_ident<'_>) -> Self { + use jacquard_common::IntoStatic; + output.value.into_static() + } + } + }; + // Generate Collection trait impl let collection_impl = quote! { impl jacquard_common::types::collection::Collection for #ident<'_> { const NSID: &'static str = #nsid; + type Record = #record_marker_ident; } }; Ok(quote! { #struct_def #(#unions)* + #output_wrapper + #record_marker #collection_impl + #from_impl }) } } @@ -127,8 +196,12 @@ impl<'c> CodeGenerator<'c> { match field_type { LexObjectProperty::Union(union) => { // Skip empty, single-variant unions unless they're self-referential - if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, &type_name, union)) { - let union_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); + if !union.refs.is_empty() + && (union.refs.len() > 1 + || self.is_self_referential_union(nsid, &type_name, union)) + { + let union_name = + self.generate_field_type_name(nsid, &type_name, field_name, ""); let refs: Vec<_> = union.refs.iter().cloned().collect(); let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; @@ -136,7 +209,8 @@ impl<'c> CodeGenerator<'c> { } } LexObjectProperty::Object(nested_obj) => { - let object_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); + let object_name = + self.generate_field_type_name(nsid, &type_name, field_name, ""); let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; unions.push(obj_def); } @@ -144,9 +218,11 @@ impl<'c> CodeGenerator<'c> { if let LexArrayItem::Union(union) = &array.items { // Skip single-variant array unions if union.refs.len() > 1 { - let union_name = self.generate_field_type_name(nsid, &type_name, field_name, "Item"); + let union_name = + self.generate_field_type_name(nsid, &type_name, field_name, "Item"); let refs: Vec<_> = union.refs.iter().cloned().collect(); - let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; + let union_def = + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; unions.push(union_def); } } @@ -296,11 +372,12 @@ impl<'c> CodeGenerator<'c> { }; // Parse ref to get NSID and def name - let (ref_nsid_str, ref_def) = if let Some((nsid, fragment)) = normalized_ref.split_once('#') { - (nsid, fragment) - } else { - (normalized_ref.as_str(), "main") - }; + let (ref_nsid_str, ref_def) = + if let Some((nsid, fragment)) = normalized_ref.split_once('#') { + (nsid, fragment) + } else { + (normalized_ref.as_str(), "main") + }; // Skip unknown refs - they'll be handled by Unknown variant if !self.corpus.ref_exists(&normalized_ref) { @@ -329,7 +406,11 @@ impl<'c> CodeGenerator<'c> { // For other fragments, include the last NSID segment to avoid collisions // e.g. app.bsky.embed.images#view -> ImagesView // app.bsky.embed.video#view -> VideoView - format!("{}{}", last_segment.to_pascal_case(), ref_def.to_pascal_case()) + format!( + "{}{}", + last_segment.to_pascal_case(), + ref_def.to_pascal_case() + ) }; variant_infos.push(VariantInfo { diff --git a/crates/jacquard-lexicon/src/lib.rs b/crates/jacquard-lexicon/src/lib.rs index 32d85e16..76e12f73 100644 --- a/crates/jacquard-lexicon/src/lib.rs +++ b/crates/jacquard-lexicon/src/lib.rs @@ -1,3 +1,41 @@ +//! # Lexicon schema parsing and Rust code generation for the Jacquard atproto ecosystem +//! +//! This crate also provides lexicon fetching capabilitiees ofr +//! +//! ## Usage +//! +//! ### Fetch lexicons +//! +//! The `lex-fetch` binary downloads lexicons from configured sources and +//! runs the code generation pipeline on them: +//! +//! ```bash +//! cargo run -p jacquard-lexicon --bin lex-fetch +//! ``` +//! +//! Configuration lives in `lexicons.kdl` at the workspace root. +//! +//! ### Generate Rust code +//! +//! The `jacquard-codegen` binary can be pointed at a local directory to +//! runs the code generation pipeline: +//! +//! ```bash +//! cargo run -p jacquard-lexicon --bin jacquard-codegen -- \ +//! -i ./lexicons \ +//! -o ./crates/jacquard-api/src +//! ``` +//! +//! +//! ## Modules +//! +//! - [`codegen`] - Rust code generation from parsed schemas +//! - [`corpus`] - Lexicon corpus management and namespace organization +//! - [`lexicon`] - Schema parsing and validation +//! - [`union_registry`] - Tracks union types for collision detection +//! - [`fetch`] - Ingests lexicons from git, atproto, http fetch, and other sources +//! - [`fs`] - Filesystem utilities for lexicon storage + pub mod codegen; pub mod corpus; pub mod error; diff --git a/crates/jacquard-oauth/src/client.rs b/crates/jacquard-oauth/src/client.rs index b4cdf47f..e39289b2 100644 --- a/crates/jacquard-oauth/src/client.rs +++ b/crates/jacquard-oauth/src/client.rs @@ -13,13 +13,13 @@ use jacquard_common::{ AuthorizationToken, CowStr, IntoStatic, error::{AuthError, ClientError, TransportError, XrpcResult}, http_client::HttpClient, - types::did::Did, + types::{did::Did, string::Handle}, xrpc::{ CallOptions, Response, XrpcClient, XrpcExt, XrpcRequest, XrpcResp, build_http_request, process_response, }, }; -use jacquard_identity::JacquardResolver; +use jacquard_identity::{JacquardResolver, resolver::IdentityResolver}; use jose_jwk::JwkSet; use std::sync::Arc; use tokio::sync::RwLock; @@ -39,6 +39,52 @@ impl OAuthClient { let client = JacquardResolver::default(); Self::new_from_resolver(store, client, client_data) } + + /// Create an OAuth client with the provided store and default localhost client metadata. + /// + /// This is a convenience constructor for quickly setting up an OAuth client + /// with default localhost redirect URIs and "atproto transition:generic" scopes. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard_oauth::client::OAuthClient; + /// # use jacquard_oauth::authstore::MemoryAuthStore; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let store = MemoryAuthStore::new(); + /// let oauth = OAuthClient::with_default_config(store); + /// # Ok(()) + /// # } + /// ``` + pub fn with_default_config(store: S) -> Self { + let client_data = ClientData { + keyset: None, + config: crate::atproto::AtprotoClientMetadata::default_localhost(), + }; + Self::new(store, client_data) + } +} + +impl OAuthClient { + /// Create an OAuth client with an in-memory auth store and default localhost client metadata. + /// + /// This is a convenience constructor for simple testing and development. + /// The session will not persist across restarts. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard_oauth::client::OAuthClient; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let oauth = OAuthClient::with_memory_store(); + /// # Ok(()) + /// # } + /// ``` + pub fn with_memory_store() -> Self { + Self::with_default_config(crate::authstore::MemoryAuthStore::new()) + } } impl OAuthClient @@ -430,3 +476,34 @@ fn is_invalid_token_response(response: &XrpcResult>) -> _ => false, } } + +impl IdentityResolver for OAuthSession +where + S: ClientAuthStore + Send + Sync + 'static, + T: OAuthResolver + IdentityResolver + XrpcExt + Send + Sync + 'static, +{ + fn options(&self) -> &jacquard_identity::resolver::ResolverOptions { + self.client.options() + } + + fn resolve_handle( + &self, + handle: &Handle<'_>, + ) -> impl Future< + Output = std::result::Result, jacquard_identity::resolver::IdentityError>, + > { + async { self.client.resolve_handle(handle).await } + } + + fn resolve_did_doc( + &self, + did: &Did<'_>, + ) -> impl Future< + Output = std::result::Result< + jacquard_identity::resolver::DidDocResponse, + jacquard_identity::resolver::IdentityError, + >, + > { + async { self.client.resolve_did_doc(did).await } + } +} diff --git a/crates/jacquard-oauth/src/lib.rs b/crates/jacquard-oauth/src/lib.rs index e6eb0c82..27c77cbd 100644 --- a/crates/jacquard-oauth/src/lib.rs +++ b/crates/jacquard-oauth/src/lib.rs @@ -1,5 +1,50 @@ -//! Core OAuth 2.1 (AT Protocol profile) types and helpers for Jacquard. -//! Transport, discovery, and orchestration live in `jacquard`. +//! # Jacquard OAuth 2.1 implementation for the AT Protocol +//! +//! Implements the AT Protocol OAuth profile, including DPoP (Demonstrating +//! Proof-of-Possession), PKCE, PAR (Pushed Authorization Requests), and token management. +//! +//! +//! ## Authentication flow +//! +//! ```no_run +//! # #[cfg(feature = "loopback")] +//! # async fn example() -> Result<(), Box> { +//! use jacquard_oauth::client::OAuthClient; +//! use jacquard_oauth::session::ClientData; +//! use jacquard_oauth::atproto::AtprotoClientMetadata; +//! use jacquard_oauth::loopback::LoopbackConfig; +//! use jacquard_oauth::authstore::MemoryAuthStore; +//! +//! let store = MemoryAuthStore::new(); +//! +//! // Create client with metadata +//! let client_data = ClientData { +//! keyset: None, // Will generate ES256 keypair if needed +//! config: AtprotoClientMetadata::default_localhost(), +//! }; +//! let oauth = OAuthClient::new(store, client_data); +//! +//! // Start auth flow (with loopback feature) +//! let session = oauth.login_with_local_server( +//! "alice.bsky.social", +//! Default::default(), +//! LoopbackConfig::default(), +//! ).await?; +//! +//! // Session handles token refresh automatically +//! # Ok(()) +//! # } +//! ``` +//! +//! ## AT Protocol specifics +//! +//! The AT Protocol OAuth profile adds: +//! - Required DPoP for all token requests +//! - PAR (Pushed Authorization Requests) for better security +//! - Specific scope format (`atproto`, `transition:generic`, etc.) +//! - Server metadata discovery at `/.well-known/oauth-authorization-server` +//! +//! See [`atproto`] module for AT Protocol-specific metadata helpers. pub mod atproto; pub mod authstore; diff --git a/crates/jacquard/Cargo.toml b/crates/jacquard/Cargo.toml index e70c85e6..f036a7ab 100644 --- a/crates/jacquard/Cargo.toml +++ b/crates/jacquard/Cargo.toml @@ -12,7 +12,7 @@ exclude.workspace = true license.workspace = true [features] -default = ["api_full", "dns", "fancy", "loopback"] +default = ["api_full", "dns", "loopback"] derive = ["dep:jacquard-derive"] api = ["jacquard-api/com_atproto", "jacquard-api/com_bad_example" ] api_bluesky = ["api", "jacquard-api/bluesky" ] @@ -27,10 +27,53 @@ loopback = ["jacquard-oauth/loopback", "jacquard-oauth/browser-open"] name = "jacquard" path = "src/lib.rs" -[[bin]] -name = "jacquard" -path = "src/main.rs" +[[example]] +name = "oauth_timeline" +path = "../../examples/oauth_timeline.rs" +required-features = ["fancy", "loopback", "api_bluesky"] + +[[example]] +name = "create_post" +path = "../../examples/create_post.rs" +required-features = ["fancy", "loopback", "api_bluesky"] + +[[example]] +name = "post_with_image" +path = "../../examples/post_with_image.rs" +required-features = ["fancy", "loopback", "api_bluesky"] + +[[example]] +name = "update_profile" +path = "../../examples/update_profile.rs" +required-features = ["fancy", "loopback", "api_bluesky"] + +[[example]] +name = "public_atproto_feed" +path = "../../examples/public_atproto_feed.rs" + +[[example]] +name = "create_whitewind_post" +path = "../../examples/create_whitewind_post.rs" +required-features = ["fancy", "loopback", "api_full"] + +[[example]] +name = "read_whitewind_posts" +path = "../../examples/read_whitewind_posts.rs" +required-features = ["fancy", "api_full"] + +[[example]] +name = "read_tangled_repo" +path = "../../examples/read_tangled_repo.rs" +required-features = ["api_full"] + +[[example]] +name = "resolve_did" +path = "../../examples/resolve_did.rs" +[[example]] +name = "update_preferences" +path = "../../examples/update_preferences.rs" +required-features = ["fancy", "loopback", "api_full"] [dependencies] jacquard-api = { version = "0.4", path = "../jacquard-api" } diff --git a/crates/jacquard/src/client.rs b/crates/jacquard/src/client.rs index 14d4c9b0..53762a1b 100644 --- a/crates/jacquard/src/client.rs +++ b/crates/jacquard/src/client.rs @@ -1,23 +1,47 @@ //! XRPC client implementation for AT Protocol //! -//! This module provides HTTP and XRPC client traits along with an authenticated -//! client implementation that manages session tokens. +//! This module provides HTTP and XRPC client traits along with session management +//! for both app-password and OAuth authentication. +//! +//! ## Key types +//! +//! - [`Agent`] - Unified session wrapper with convenience methods +//! - [`CredentialSession`] - App-password authentication with auto-refresh +//! - [`crate::oauth::client::OAuthSession`] - OAuth/DPoP authentication +//! - [`AgentSession`] - Common trait for both session types +//! +//! ## Modules +//! +//! - [`credential_session`] - App-password session implementation +//! - [`token`] - Token storage and persistence +//! - [`vec_update`] - Trait for fetch-modify-put patterns on array endpoints -/// Stateful session client for app‑password auth with auto‑refresh. +/// App-password session implementation with auto-refresh pub mod credential_session; -/// Token storage and on‑disk formats shared across app‑password and OAuth. +/// Token storage and on-disk persistence formats pub mod token; +/// Trait for fetch-modify-put patterns on array-based endpoints +pub mod vec_update; use core::future::Future; +use jacquard_api::com_atproto::repo::create_record::CreateRecordOutput; +use jacquard_api::com_atproto::repo::delete_record::DeleteRecordOutput; +use jacquard_api::com_atproto::repo::put_record::PutRecordOutput; use jacquard_api::com_atproto::server::create_session::CreateSessionOutput; use jacquard_api::com_atproto::server::refresh_session::RefreshSessionOutput; -use jacquard_common::AuthorizationToken; use jacquard_common::error::TransportError; pub use jacquard_common::error::{ClientError, XrpcResult}; use jacquard_common::http_client::HttpClient; pub use jacquard_common::session::{MemorySessionStore, SessionStore, SessionStoreError}; -use jacquard_common::xrpc::{CallOptions, Response, XrpcClient, XrpcExt, XrpcRequest}; +use jacquard_common::types::blob::{BlobRef, MimeType}; +use jacquard_common::types::collection::Collection; +use jacquard_common::types::recordkey::{RecordKey, Rkey}; +use jacquard_common::types::string::AtUri; +use jacquard_common::xrpc::{ + CallOptions, Response, XrpcClient, XrpcError, XrpcExt, XrpcRequest, XrpcResp, +}; +use jacquard_common::{AuthorizationToken, xrpc}; use jacquard_common::{ CowStr, IntoStatic, types::string::{Did, Handle}, @@ -28,10 +52,100 @@ use jacquard_oauth::client::OAuthSession; use jacquard_oauth::dpop::DpopExt; use jacquard_oauth::resolver::OAuthResolver; +use serde::Serialize; pub use token::FileAuthStore; use crate::client::credential_session::{CredentialSession, SessionKey}; +use jacquard_common::error::{AuthError, DecodeError}; +use jacquard_common::types::nsid::Nsid; +use jacquard_common::xrpc::GenericXrpcError; + +/// Error type for Agent convenience methods +#[derive(Debug, thiserror::Error, miette::Diagnostic)] +pub enum AgentError { + /// Transport/network layer failure + #[error(transparent)] + #[diagnostic(transparent)] + Client(#[from] ClientError), + + /// No session available for operations requiring authentication + #[error("No session available - cannot determine repo")] + NoSession, + + /// Authentication error from XRPC layer + #[error("Authentication error: {0}")] + #[diagnostic(transparent)] + Auth( + #[from] + #[diagnostic_source] + AuthError, + ), + + /// Generic XRPC error (InvalidRequest, etc.) + #[error("XRPC error: {0}")] + Generic(GenericXrpcError), + + /// Response deserialization failed + #[error("Failed to decode response: {0}")] + #[diagnostic(transparent)] + Decode( + #[from] + #[diagnostic_source] + DecodeError, + ), + + /// Record operation failed with typed error from endpoint + /// Context: which repo/collection/rkey we were operating on + #[error("Record operation failed on {collection}/{rkey:?} in repo {repo}: {error}")] + RecordOperation { + /// The repository DID + repo: Did<'static>, + /// The collection NSID + collection: Nsid<'static>, + /// The record key + rkey: RecordKey>, + /// The underlying error + error: Box, + }, + + /// Multi-step operation failed at sub-step (e.g., get failed in update_record) + #[error("Operation failed at step '{step}': {error}")] + SubOperation { + /// Description of which step failed + step: &'static str, + /// The underlying error + error: Box, + }, +} + +impl IntoStatic for AgentError { + type Output = AgentError; + + fn into_static(self) -> Self::Output { + match self { + AgentError::RecordOperation { + repo, + collection, + rkey, + error, + } => AgentError::RecordOperation { + repo: repo.into_static(), + collection: collection.into_static(), + rkey: rkey.into_static(), + error, + }, + AgentError::SubOperation { step, error } => AgentError::SubOperation { step, error }, + // Error types are already 'static + AgentError::Client(e) => AgentError::Client(e), + AgentError::NoSession => AgentError::NoSession, + AgentError::Auth(e) => AgentError::Auth(e), + AgentError::Generic(e) => AgentError::Generic(e), + AgentError::Decode(e) => AgentError::Decode(e), + } + } +} + /// App password session information from `com.atproto.server.createSession` /// /// Contains the access and refresh tokens along with user identity information. @@ -204,6 +318,434 @@ impl Agent { pub async fn refresh(&self) -> Result, ClientError> { self.inner.refresh().await } + + // Convenience methods for repository operations + + /// Create a new record in the repository. + /// + /// The collection is inferred from the record type's `Collection::NSID`. + /// The repo is automatically filled from the session info. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard::client::BasicClient; + /// # use jacquard_api::app_bsky::feed::post::Post; + /// # use jacquard_common::types::string::Datetime; + /// # use jacquard_common::CowStr; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let agent: BasicClient = todo!(); + /// let post = Post { + /// text: CowStr::from("Hello world!"), + /// created_at: Datetime::now(), + /// embed: None, + /// entities: None, + /// facets: None, + /// labels: None, + /// langs: None, + /// reply: None, + /// tags: None, + /// extra_data: Default::default(), + /// }; + /// let output = agent.create_record(post, None).await?; + /// println!("Created record: {}", output.uri); + /// # Ok(()) + /// # } + /// ``` + pub async fn create_record( + &self, + record: R, + rkey: Option>>, + ) -> Result, AgentError> + where + R: Collection + serde::Serialize, + { + use jacquard_api::com_atproto::repo::create_record::CreateRecord; + use jacquard_common::types::ident::AtIdentifier; + use jacquard_common::types::value::to_data; + + let (did, _) = self.info().await.ok_or(AgentError::NoSession)?; + + let data = to_data(&record).map_err(|e| AgentError::SubOperation { + step: "serialize record", + error: Box::new(e), + })?; + + let request = CreateRecord::new() + .repo(AtIdentifier::Did(did)) + .collection(R::nsid()) + .record(data) + .maybe_rkey(rkey) + .build(); + + let response = self.send(request).await?; + response.into_output().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(typed) => AgentError::SubOperation { + step: "create record", + error: Box::new(typed), + }, + }) + } + + /// Delete a record from the repository. + /// + /// The collection is inferred from the type parameter. + /// The repo is automatically filled from the session info. + pub async fn delete_record( + &self, + rkey: K, + ) -> Result, AgentError> + where + R: Collection, + K: Into>>, + { + use jacquard_api::com_atproto::repo::delete_record::DeleteRecord; + use jacquard_common::types::ident::AtIdentifier; + + let (did, _) = self.info().await.ok_or(AgentError::NoSession)?; + + let request = DeleteRecord::new() + .repo(AtIdentifier::Did(did)) + .collection(R::nsid()) + .rkey(rkey.into()) + .build(); + + let response = self.send(request).await?; + response.into_output().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(typed) => AgentError::SubOperation { + step: "delete record", + error: Box::new(typed), + }, + }) + } + + /// Put (upsert) a record in the repository. + /// + /// The collection is inferred from the record type's `Collection::NSID`. + /// The repo is automatically filled from the session info. + pub async fn put_record( + &self, + rkey: RecordKey>, + record: R, + ) -> Result, AgentError> + where + R: Collection + serde::Serialize, + { + use jacquard_api::com_atproto::repo::put_record::PutRecord; + use jacquard_common::types::ident::AtIdentifier; + use jacquard_common::types::value::to_data; + + let (did, _) = self.info().await.ok_or(AgentError::NoSession)?; + + let data = to_data(&record).map_err(|e| AgentError::SubOperation { + step: "serialize record", + error: Box::new(e), + })?; + + let request = PutRecord::new() + .repo(AtIdentifier::Did(did)) + .collection(R::nsid()) + .rkey(rkey) + .record(data) + .build(); + + let response = self.send(request).await?; + response.into_output().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(typed) => AgentError::SubOperation { + step: "put record", + error: Box::new(typed), + }, + }) + } + + /// Upload a blob to the repository. + /// + /// The mime type is sent as a Content-Type header hint, though the server also performs + /// its own inference. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard::client::BasicClient; + /// # use jacquard_common::types::blob::MimeType; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let agent: BasicClient = todo!(); + /// let data = std::fs::read("image.png")?; + /// let mime_type = MimeType::new_static("image/png"); + /// let blob_ref = agent.upload_blob(data, mime_type).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn upload_blob( + &self, + data: impl Into, + mime_type: MimeType<'_>, + ) -> Result, AgentError> { + use http::header::CONTENT_TYPE; + use jacquard_api::com_atproto::repo::upload_blob::UploadBlob; + + let bytes = data.into(); + let request = UploadBlob::new().body(bytes).build(); + + // Override Content-Type header with actual mime type instead of */* + let base = self.base_uri(); + let mut opts = self.opts().await; + opts.extra_headers.push(( + CONTENT_TYPE, + http::HeaderValue::from_str(mime_type.as_str()).map_err(|e| { + AgentError::SubOperation { + step: "set Content-Type header", + error: Box::new(e), + } + })?, + )); + + let response = self.xrpc(base).with_options(opts).send(&request).await?; + + let output = response.into_output().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(typed) => AgentError::SubOperation { + step: "upload blob", + error: Box::new(typed), + }, + })?; + Ok(BlobRef::Blob(output.blob.into_static())) + } + + /// Update a vec-based data structure with a fetch-modify-put pattern. + /// + /// This is useful for endpoints like preferences that return arrays requiring + /// fetch-modify-put operations. + /// + /// # Example + /// + /// ```ignore + /// agent.update_vec::(|prefs| { + /// prefs.push(AdultContentPref::new().enabled(true).build().into()); + /// prefs.retain(|p| !matches!(p, Preference::Hidden(_))); + /// }).await?; + /// ``` + pub async fn update_vec<'s, U>( + &'s self, + modify: impl FnOnce(&mut Vec<::Item>), + ) -> Result as XrpcRequest<'s>>::Response>, AgentError> + where + U: vec_update::VecUpdate + 's, + { + // Fetch current data + let get_request = U::build_get(); + let response = self.send(get_request).await?; + let output = response.parse().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(_) => AgentError::SubOperation { + step: "get vec", + error: format!("{:?}", e).into(), + }, + })?; + + // Extract vec (converts to owned via IntoStatic) + let mut items = U::extract_vec(output); + + // Apply modification + modify(&mut items); + + // Build put request + let put_request = U::build_put(items); + + // Send it + Ok(self.send(put_request).await?) + } + + /// Update a single item in a vec-based data structure. + /// + /// This is a convenience wrapper around `update_vec` that finds and replaces + /// a single matching item, or appends it if not found. + /// + /// # Example + /// + /// ```ignore + /// let pref = AdultContentPref::new().enabled(true).build(); + /// agent.update_vec_item::(pref.into()).await?; + /// ``` + pub async fn update_vec_item<'s, U>( + &'s self, + item: ::Item, + ) -> Result as XrpcRequest<'s>>::Response>, AgentError> + where + U: vec_update::VecUpdate + 's, + { + self.update_vec::(|vec| { + if let Some(pos) = vec.iter().position(|i| U::matches(i, &item)) { + vec[pos] = item; + } else { + vec.push(item); + } + }) + .await + } +} + +impl Agent { + /// Get a record from the repository using an at:// URI. + /// + /// Returns a typed `Response` that deserializes directly to the record type. + /// Use `.parse()` to borrow from the response buffer, or `.into_output()` for owned data. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard::client::BasicClient; + /// # use jacquard_api::app_bsky::feed::post::Post; + /// # use jacquard_common::types::string::AtUri; + /// # use jacquard_common::IntoStatic; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let agent: BasicClient = todo!(); + /// let uri = AtUri::new_static("at://did:plc:xyz/app.bsky.feed.post/3l5bqm7lepk2c").unwrap(); + /// let response = agent.get_record::(uri).await?; + /// let output = response.parse()?; // PostGetRecordOutput<'_> borrowing from buffer + /// println!("Post text: {}", output.value.text); + /// + /// // Or get owned data + /// let output_owned = response.into_output()?; + /// # Ok(()) + /// # } + /// ``` + pub async fn get_record(&self, uri: AtUri<'_>) -> Result, ClientError> + where + R: Collection, + { + // Validate that URI's collection matches the expected type + if let Some(uri_collection) = uri.collection() { + if uri_collection.as_str() != R::nsid().as_str() { + return Err(ClientError::Transport(TransportError::Other( + format!( + "Collection mismatch: URI contains '{}' but type parameter expects '{}'", + uri_collection, + R::nsid() + ) + .into(), + ))); + } + } + + let rkey = uri.rkey().ok_or_else(|| { + ClientError::Transport(TransportError::Other("AtUri missing rkey".into())) + })?; + + // Resolve authority (DID or handle) to get DID and PDS + use jacquard_common::types::ident::AtIdentifier; + let (repo_did, pds_url) = match uri.authority() { + AtIdentifier::Did(did) => { + let pds = self.pds_for_did(did).await.map_err(|e| { + ClientError::Transport(TransportError::Other( + format!("Failed to resolve PDS for {}: {}", did, e).into(), + )) + })?; + (did.clone(), pds) + } + AtIdentifier::Handle(handle) => self.pds_for_handle(handle).await.map_err(|e| { + ClientError::Transport(TransportError::Other( + format!("Failed to resolve handle {}: {}", handle, e).into(), + )) + })?, + }; + + // Make stateless XRPC call to that PDS (no auth required for public records) + use jacquard_api::com_atproto::repo::get_record::GetRecord; + let request = GetRecord::new() + .repo(AtIdentifier::Did(repo_did)) + .collection(R::nsid()) + .rkey(rkey.clone()) + .build(); + + let response = self.xrpc(pds_url).send(&request).await?; + Ok(response.transmute()) + } + + /// Update a record in-place with a fetch-modify-put pattern. + /// + /// This fetches the record using an at:// URI, converts it to owned data, applies + /// the modification function, and puts it back. The modification function receives + /// a mutable reference to the owned record. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard::client::BasicClient; + /// # use jacquard_api::app_bsky::actor::profile::Profile; + /// # use jacquard_common::CowStr; + /// # use jacquard_common::types::string::AtUri; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let agent: BasicClient = todo!(); + /// let uri = AtUri::new_static("at://did:plc:xyz/app.bsky.actor.profile/self").unwrap(); + /// // Update profile record in-place + /// agent.update_record::(uri, |output| { + /// output.value.display_name = Some(CowStr::from("New Name")); + /// output.value.description = Some(CowStr::from("Updated bio")); + /// }).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn update_record( + &self, + uri: AtUri<'_>, + f: impl FnOnce(&mut <<::Record as XrpcResp>::Output<'_> as IntoStatic>::Output), + ) -> Result, AgentError> + where + R: Collection + Serialize, + R: From<::Record>, + R: for<'a> From< + <<::Record as XrpcResp>::Output<'a> as IntoStatic>::Output, + >, + { + // Fetch the record - Response where R::Record::Output<'de> = R<'de> + let response = self.get_record::(uri.clone()).await?; + + // Parse to get R<'_> borrowing from response buffer + let record = response.parse().map_err(|e| match e { + XrpcError::Auth(auth) => AgentError::Auth(auth), + XrpcError::Generic(g) => AgentError::Generic(g), + XrpcError::Decode(e) => AgentError::Decode(DecodeError::Json(e)), + XrpcError::Xrpc(typed) => AgentError::SubOperation { + step: "get record", + error: format!("{:?}", typed).into(), + }, + })?; + + // Convert to owned + let mut owned = record.into_static(); + + // Apply modification + f(&mut owned); + + // Put it back + let rkey = uri + .rkey() + .ok_or(AgentError::SubOperation { + step: "extract rkey", + error: "AtUri missing rkey".into(), + })? + .clone() + .into_static(); + self.put_record::(rkey, R::from(owned)).await + } } impl HttpClient for Agent { @@ -236,6 +778,32 @@ impl XrpcClient for Agent { } } +impl IdentityResolver for Agent { + fn options(&self) -> &jacquard_identity::resolver::ResolverOptions { + self.inner.options() + } + + fn resolve_handle( + &self, + handle: &Handle<'_>, + ) -> impl Future, jacquard_identity::resolver::IdentityError>> + { + async { self.inner.resolve_handle(handle).await } + } + + fn resolve_did_doc( + &self, + did: &Did<'_>, + ) -> impl Future< + Output = Result< + jacquard_identity::resolver::DidDocResponse, + jacquard_identity::resolver::IdentityError, + >, + > { + async { self.inner.resolve_did_doc(did).await } + } +} + impl From for Agent { fn from(inner: A) -> Self { Self::new(inner) @@ -254,3 +822,39 @@ pub type BasicClient = Agent< jacquard_identity::PublicResolver, >, >; + +impl BasicClient { + /// Create an unauthenticated BasicClient for public API access. + /// + /// Uses an in-memory session store and public resolver. Suitable for + /// read-only operations on public data without authentication. + /// + /// # Example + /// + /// ```no_run + /// # use jacquard::client::BasicClient; + /// # use jacquard::types::string::AtUri; + /// # use jacquard_api::app_bsky::feed::post::Post; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = BasicClient::unauthenticated(); + /// let uri = AtUri::new_static("at://did:plc:xyz/app.bsky.feed.post/3l5abc").unwrap(); + /// let response = client.get_record::>(uri).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn unauthenticated() -> Self { + use std::sync::Arc; + let http = reqwest::Client::new(); + let resolver = jacquard_identity::PublicResolver::new(http, Default::default()); + let store = MemorySessionStore::default(); + let session = CredentialSession::new(Arc::new(store), Arc::new(resolver)); + Agent::new(session) + } +} + +impl Default for BasicClient { + fn default() -> Self { + Self::unauthenticated() + } +} diff --git a/crates/jacquard/src/client/credential_session.rs b/crates/jacquard/src/client/credential_session.rs index b2edea3b..eed641fc 100644 --- a/crates/jacquard/src/client/credential_session.rs +++ b/crates/jacquard/src/client/credential_session.rs @@ -8,7 +8,7 @@ use jacquard_common::{ error::{AuthError, ClientError, TransportError, XrpcResult}, http_client::HttpClient, session::SessionStore, - types::did::Did, + types::{did::Did, string::Handle}, xrpc::{CallOptions, Response, XrpcClient, XrpcError, XrpcExt, XrpcRequest, XrpcResp}, }; use tokio::sync::RwLock; @@ -451,3 +451,33 @@ fn is_expired(response: &XrpcResult>) -> bool { _ => false, } } + +impl IdentityResolver for CredentialSession +where + S: SessionStore + Send + Sync + 'static, + T: HttpClient + IdentityResolver + Send + Sync + 'static, +{ + fn options(&self) -> &jacquard_identity::resolver::ResolverOptions { + self.client.options() + } + + fn resolve_handle( + &self, + handle: &Handle<'_>, + ) -> impl Future, jacquard_identity::resolver::IdentityError>> + { + async { self.client.resolve_handle(handle).await } + } + + fn resolve_did_doc( + &self, + did: &Did<'_>, + ) -> impl Future< + Output = Result< + jacquard_identity::resolver::DidDocResponse, + jacquard_identity::resolver::IdentityError, + >, + > { + async { self.client.resolve_did_doc(did).await } + } +} diff --git a/crates/jacquard/src/client/token.rs b/crates/jacquard/src/client/token.rs index baa581f4..2dfe2eb9 100644 --- a/crates/jacquard/src/client/token.rs +++ b/crates/jacquard/src/client/token.rs @@ -389,7 +389,8 @@ impl FileAuthStore { } #[async_trait::async_trait] -impl jacquard_common::session::SessionStore< +impl + jacquard_common::session::SessionStore< crate::client::credential_session::SessionKey, crate::client::AtpSession, > for FileAuthStore @@ -452,8 +453,8 @@ impl jacquard_common::session::SessionStore< #[cfg(test)] mod tests { use super::*; - use crate::client::credential_session::SessionKey; use crate::client::AtpSession; + use crate::client::credential_session::SessionKey; use jacquard_common::types::string::{Did, Handle}; use std::fs; use std::path::PathBuf; diff --git a/crates/jacquard/src/client/vec_update.rs b/crates/jacquard/src/client/vec_update.rs new file mode 100644 index 00000000..53ad352b --- /dev/null +++ b/crates/jacquard/src/client/vec_update.rs @@ -0,0 +1,67 @@ +/// Bluesky actor preferences implementation +pub mod preferences; + +pub use preferences::PreferencesUpdate; + +use jacquard_common::IntoStatic; +use jacquard_common::xrpc::{XrpcRequest, XrpcResp}; + +/// Trait for get-modify-put patterns on vec-based data structures. +/// +/// This trait enables convenient update operations for endpoints that return arrays +/// that need to be fetched, modified, and put back. Common use cases include +/// preferences, saved feeds, and similar collection-style data. +/// +/// # Example +/// +/// ```ignore +/// use jacquard::client::vec_update::VecUpdate; +/// +/// struct PreferencesUpdate; +/// +/// impl VecUpdate for PreferencesUpdate { +/// type GetRequest = GetPreferences; +/// type PutRequest = PutPreferences; +/// type Item = PreferencesItem<'static>; +/// +/// fn extract_vec(output: GetPreferencesOutput<'_>) -> Vec { +/// output.preferences.into_iter().map(|p| p.into_static()).collect() +/// } +/// +/// fn build_put(items: Vec) -> PutPreferences { +/// PutPreferences { preferences: items } +/// } +/// +/// fn matches(a: &Self::Item, b: &Self::Item) -> bool { +/// // Match by enum variant discriminant +/// std::mem::discriminant(a) == std::mem::discriminant(b) +/// } +/// } +/// ``` +pub trait VecUpdate { + /// The XRPC request type for fetching the data + type GetRequest<'de>: XrpcRequest<'de>; + + /// The XRPC request type for putting the data back + type PutRequest<'de>: XrpcRequest<'de>; + + /// The item type contained in the vec (must be owned/static) + type Item: IntoStatic; + + /// Build the get request + fn build_get<'s>() -> Self::GetRequest<'s>; + + /// Extract the vec from the get response output + fn extract_vec<'s>( + output: < as XrpcRequest<'s>>::Response as XrpcResp>::Output<'s>, + ) -> Vec; + + /// Build the put request from the modified vec + fn build_put<'s>(items: Vec) -> Self::PutRequest<'s>; + + /// Check if two items match (for single-item update operations) + /// + /// This is used by `update_vec_item` to find and replace a single item in the vec. + /// For example, preferences might match by enum variant discriminant. + fn matches<'s>(a: &'s Self::Item, b: &'s Self::Item) -> bool; +} diff --git a/crates/jacquard/src/client/vec_update/preferences.rs b/crates/jacquard/src/client/vec_update/preferences.rs new file mode 100644 index 00000000..702b5dbd --- /dev/null +++ b/crates/jacquard/src/client/vec_update/preferences.rs @@ -0,0 +1,63 @@ +use jacquard_api::app_bsky::actor::PreferencesItem; +use jacquard_api::app_bsky::actor::get_preferences::{GetPreferences, GetPreferencesOutput}; +use jacquard_api::app_bsky::actor::put_preferences::PutPreferences; +use jacquard_common::IntoStatic; + +/// VecUpdate implementation for Bluesky actor preferences. +/// +/// Provides get-modify-put operations on user preferences, which are stored +/// as a vec of preference items (each identified by enum discriminant). +/// +/// # Example +/// +/// ```ignore +/// use jacquard::client::vec_update::PreferencesUpdate; +/// use jacquard_api::app_bsky::actor::PreferencesItem; +/// +/// // Update all preferences +/// agent.update_vec::(|prefs| { +/// // Add a new preference +/// prefs.push(PreferencesItem::AdultContentPref( +/// Box::new(AdultContentPref { enabled: true }) +/// )); +/// +/// // Remove by variant +/// prefs.retain(|p| !matches!(p, PreferencesItem::InterestsPref(_))); +/// }).await?; +/// +/// // Update a single preference (replaces by discriminant) +/// let pref = PreferencesItem::AdultContentPref( +/// Box::new(AdultContentPref { enabled: false }) +/// ); +/// agent.update_vec_item::(pref).await?; +/// ``` +pub struct PreferencesUpdate; + +impl super::VecUpdate for PreferencesUpdate { + type GetRequest<'de> = GetPreferences; + type PutRequest<'de> = PutPreferences<'de>; + type Item = PreferencesItem<'static>; + + fn build_get<'s>() -> Self::GetRequest<'s> { + GetPreferences::new().build() + } + + fn extract_vec<'s>( + output: GetPreferencesOutput<'s>, + ) -> Vec<::Output> { + output + .preferences + .into_iter() + .map(|p| p.into_static()) + .collect() + } + + fn build_put<'s>(items: Vec<::Output>) -> Self::PutRequest<'s> { + PutPreferences::new().preferences(items).build() + } + + fn matches<'s>(a: &'s Self::Item, b: &'s Self::Item) -> bool { + // Match preferences by enum variant discriminant + std::mem::discriminant(a) == std::mem::discriminant(b) + } +} diff --git a/crates/jacquard/src/lib.rs b/crates/jacquard/src/lib.rs index a85f746f..d6b7e1f4 100644 --- a/crates/jacquard/src/lib.rs +++ b/crates/jacquard/src/lib.rs @@ -49,17 +49,8 @@ //! async fn main() -> miette::Result<()> { //! let args = Args::parse(); //! -//! // File-backed auth store shared by OAuthClient and session registry -//! let store = FileAuthStore::new(&args.store); -//! let client_data = jacquard_oauth::session::ClientData { -//! keyset: None, -//! // Default sets normal localhost redirect URIs and "atproto transition:generic" scopes. -//! // The localhost helper will ensure you have at least "atproto" and will fix urls -//! config: AtprotoClientMetadata::default_localhost(), -//! }; -//! -//! // Build an OAuth client (this is reusable, and can create multiple sessions) -//! let oauth = OAuthClient::new(store, client_data); +//! // Build an OAuth client with file-backed auth store and default localhost config +//! let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); //! // Authenticate with a PDS, using a loopback server to handle the callback flow //! # #[cfg(feature = "loopback")] //! let session = oauth @@ -155,16 +146,29 @@ //! Ok(()) //! } //! ``` +//! +//! ## Component Crates +//! +//! Jacquard is split into several crates for modularity. The main `jacquard` crate +//! re-exports most of the others, so you typically only need to depend on it directly. +//! +//! - [`jacquard-common`] - AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.) +//! - [`jacquard-api`] - Generated API bindings from 646+ lexicon schemas +//! - [`jacquard-axum`] - Server-side XRPC handler extractors for Axum framework (not re-exported, depends on jacquard) +//! - [`jacquard-oauth`] - OAuth/DPoP flow implementation with session management +//! - [`jacquard-identity`] - Identity resolution (handle→DID, DID→Doc, OAuth metadata) +//! - [`jacquard-lexicon`] - Lexicon resolution, fetching, parsing and Rust code generation from schemas +//! - [`jacquard-derive`] - Macros (`#[lexicon]`, `#[open_union]`, `#[derive(IntoStatic)]`) #![warn(missing_docs)] -/// XRPC client traits and basic implementation pub mod client; +pub use common::*; #[cfg(feature = "api")] /// If enabled, re-export the generated api crate pub use jacquard_api as api; -pub use jacquard_common::*; +pub use jacquard_common as common; #[cfg(feature = "derive")] /// if enabled, reexport the attribute macros diff --git a/examples/axum_server.rs b/examples/axum_server.rs new file mode 100644 index 00000000..34d307b0 --- /dev/null +++ b/examples/axum_server.rs @@ -0,0 +1,36 @@ +use axum::Router; +use jacquard::api::com_atproto::identity::resolve_did::ResolveDidRequest; +use jacquard_axum::{ExtractXrpc, IntoRouter}; +use miette::{IntoDiagnostic, Result}; +use tracing_subscriber::EnvFilter; + +#[axum_macros::debug_handler] +async fn handler(ExtractXrpc(_args): ExtractXrpc) -> &'static str { + "hello world!" + // let res = jacquard::identity::slingshot_resolver_default(); + // let doc = res.resolve_did_doc(&args.did).await?; + // let valid_doc = doc.parse()?; + // let doc_value = serde_json::to_value(valid_doc).unwrap(); + // Ok(ResolveDidOutput { + // did_doc: Data::from_json(&doc_value).unwrap().into_static(), + // extra_data: Default::default(), + // } + // .into()) +} + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt() + .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc_3339()) + .with_env_filter(EnvFilter::from_env("QDPDS_LOG")) + .init(); + let app = Router::new() + .route("/", axum::routing::get(|| async { "hello world!" })) + .merge(ResolveDidRequest::into_router(handler)) + .layer(tower_http::trace::TraceLayer::new_for_http()); + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") + .await + .into_diagnostic()?; + axum::serve(listener, app).await.unwrap(); + Ok(()) +} diff --git a/examples/create_post.rs b/examples/create_post.rs new file mode 100644 index 00000000..0fd22de0 --- /dev/null +++ b/examples/create_post.rs @@ -0,0 +1,56 @@ +use clap::Parser; +use jacquard::api::app_bsky::feed::post::Post; +use jacquard::client::{Agent, FileAuthStore}; +use jacquard::oauth::atproto::AtprotoClientMetadata; +use jacquard::oauth::client::OAuthClient; +use jacquard::oauth::loopback::LoopbackConfig; +use jacquard::types::string::Datetime; +use jacquard::xrpc::XrpcClient; +use jacquard::CowStr; +use miette::IntoDiagnostic; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Create a simple post")] +struct Args { + /// Handle (e.g., alice.bsky.social), DID, or PDS URL + input: CowStr<'static>, + + /// Post text + #[arg(short, long)] + text: String, + + /// Path to auth store file (will be created if missing) + #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] + store: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); + let session = oauth + .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) + .await?; + + let agent: Agent<_> = Agent::from(session); + + // Create a simple text post using the Agent convenience method + let post = Post { + text: CowStr::from(args.text), + created_at: Datetime::now(), + embed: None, + entities: None, + facets: None, + labels: None, + langs: None, + reply: None, + tags: None, + extra_data: Default::default(), + }; + + let output = agent.create_record(post, None).await?; + println!("✓ Created post: {}", output.uri); + + Ok(()) +} diff --git a/examples/create_whitewind_post.rs b/examples/create_whitewind_post.rs new file mode 100644 index 00000000..9e805ecd --- /dev/null +++ b/examples/create_whitewind_post.rs @@ -0,0 +1,66 @@ +use clap::Parser; +use jacquard::api::com_whtwnd::blog::entry::Entry; +use jacquard::client::{Agent, FileAuthStore}; +use jacquard::oauth::atproto::AtprotoClientMetadata; +use jacquard::oauth::client::OAuthClient; +use jacquard::oauth::loopback::LoopbackConfig; +use jacquard::types::string::Datetime; +use jacquard::xrpc::XrpcClient; +use jacquard::CowStr; +use miette::IntoDiagnostic; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Create a WhiteWind blog post")] +struct Args { + /// Handle (e.g., alice.bsky.social), DID, or PDS URL + input: CowStr<'static>, + + /// Blog post title + #[arg(short, long)] + title: String, + + /// Blog post content (markdown) + #[arg(short, long)] + content: String, + + /// Optional subtitle + #[arg(short, long)] + subtitle: Option, + + /// Path to auth store file (will be created if missing) + #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] + store: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); + let session = oauth + .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) + .await?; + + let agent: Agent<_> = Agent::from(session); + + // Create a WhiteWind blog entry + // The content field accepts markdown + let entry = Entry { + title: Some(CowStr::from(args.title)), + subtitle: args.subtitle.map(CowStr::from), + content: CowStr::from(args.content), + created_at: Some(Datetime::now()), + visibility: Some(CowStr::from("url")), // "url" = public with link, "author" = public on profile + theme: None, + ogp: None, + blobs: None, + is_draft: None, + extra_data: Default::default(), + }; + + let output = agent.create_record(entry, None).await?; + println!("✓ Created WhiteWind blog post: {}", output.uri); + println!(" View at: https://whtwnd.com/post/{}", output.uri); + + Ok(()) +} diff --git a/crates/jacquard/src/main.rs b/examples/oauth_timeline.rs similarity index 94% rename from crates/jacquard/src/main.rs rename to examples/oauth_timeline.rs index 4af5803c..89748241 100644 --- a/crates/jacquard/src/main.rs +++ b/examples/oauth_timeline.rs @@ -26,10 +26,8 @@ struct Args { async fn main() -> miette::Result<()> { let args = Args::parse(); - // File-backed auth store for testing + // File-backed auth store shared by OAuthClient and session registry let store = FileAuthStore::new(&args.store); - - // Minimal localhost client metadata (redirect_uris get set by loopback helper) let client_data = jacquard_oauth::session::ClientData { keyset: None, // Default sets normal localhost redirect URIs and "atproto transition:generic" scopes. @@ -37,7 +35,7 @@ async fn main() -> miette::Result<()> { config: AtprotoClientMetadata::default_localhost(), }; - // Build an OAuth client + // Build an OAuth client (this is reusable, and can create multiple sessions) let oauth = OAuthClient::new(store, client_data); #[cfg(feature = "loopback")] diff --git a/examples/post_with_image.rs b/examples/post_with_image.rs new file mode 100644 index 00000000..98d139ba --- /dev/null +++ b/examples/post_with_image.rs @@ -0,0 +1,97 @@ +use clap::Parser; +use jacquard::api::app_bsky::embed::images::{Image, Images}; +use jacquard::api::app_bsky::feed::post::{Post, PostEmbed}; +use jacquard::client::{Agent, FileAuthStore}; +use jacquard::oauth::atproto::AtprotoClientMetadata; +use jacquard::oauth::client::OAuthClient; +use jacquard::oauth::loopback::LoopbackConfig; +use jacquard::types::blob::MimeType; +use jacquard::types::string::Datetime; +use jacquard::xrpc::XrpcClient; +use jacquard::CowStr; +use miette::IntoDiagnostic; +use std::path::PathBuf; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Create a post with an image")] +struct Args { + /// Handle (e.g., alice.bsky.social), DID, or PDS URL + input: CowStr<'static>, + + /// Post text + #[arg(short, long)] + text: String, + + /// Path to image file + #[arg(short, long)] + image: PathBuf, + + /// Alt text for image + #[arg(long)] + alt: Option, + + /// Path to auth store file (will be created if missing) + #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] + store: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); + let session = oauth + .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) + .await?; + + let agent: Agent<_> = Agent::from(session); + + // Read image file + let image_data = std::fs::read(&args.image).into_diagnostic()?; + + // Infer mime type from extension + let mime_str = match args.image.extension().and_then(|s| s.to_str()) { + Some("jpg") | Some("jpeg") => "image/jpeg", + Some("png") => "image/png", + Some("gif") => "image/gif", + Some("webp") => "image/webp", + _ => "image/jpeg", // default + }; + let mime_type = MimeType::new_static(mime_str); + + println!("📤 Uploading image..."); + let blob_ref = agent.upload_blob(image_data, mime_type).await?; + + // Extract the Blob from the BlobRef + let blob = match blob_ref { + jacquard::types::blob::BlobRef::Blob(b) => b, + _ => miette::bail!("Expected Blob, got LegacyBlob"), + }; + + // Create post with image embed + let post = Post { + text: CowStr::from(args.text), + created_at: Datetime::now(), + embed: Some(PostEmbed::Images(Box::new(Images { + images: vec![Image { + alt: CowStr::from(args.alt.unwrap_or_default()), + image: blob, + aspect_ratio: None, + extra_data: Default::default(), + }], + extra_data: Default::default(), + }))), + entities: None, + facets: None, + labels: None, + langs: None, + reply: None, + tags: None, + extra_data: Default::default(), + }; + + let output = agent.create_record(post, None).await?; + println!("✓ Created post with image: {}", output.uri); + + Ok(()) +} diff --git a/examples/public_atproto_feed.rs b/examples/public_atproto_feed.rs new file mode 100644 index 00000000..ee8fb198 --- /dev/null +++ b/examples/public_atproto_feed.rs @@ -0,0 +1,32 @@ +use jacquard::api::app_bsky::feed::get_feed::GetFeed; +use jacquard::api::app_bsky::feed::post::Post; +use jacquard::types::string::AtUri; +use jacquard::types::value::from_data; +use jacquard::xrpc::XrpcExt; +use miette::IntoDiagnostic; + +#[tokio::main] +async fn main() -> miette::Result<()> { + // Stateless XRPC - no auth required for public feeds + let http = reqwest::Client::new(); + let base = url::Url::parse("https://public.api.bsky.app").into_diagnostic()?; + + // Feed of posts about the AT Protocol + let feed_uri = + AtUri::new_static("at://did:plc:oio4hkxaop4ao4wz2pp3f4cr/app.bsky.feed.generator/atproto") + .unwrap(); + + let request = GetFeed::new().feed(feed_uri).limit(10).build(); + + let response = http.xrpc(base).send(&request).await?; + let output = response.into_output()?; + + println!("📰 Latest posts from the AT Protocol feed:\n"); + for (i, item) in output.feed.iter().enumerate() { + // Deserialize the post record from the Data type + let post: Post = from_data(&item.post.record).into_diagnostic()?; + println!("{}.(@{})\n{} ", i + 1, item.post.author.handle, post.text); + } + + Ok(()) +} diff --git a/examples/read_tangled_repo.rs b/examples/read_tangled_repo.rs new file mode 100644 index 00000000..abe9ff13 --- /dev/null +++ b/examples/read_tangled_repo.rs @@ -0,0 +1,63 @@ +use clap::Parser; +use jacquard::api::sh_tangled::repo::Repo; +use jacquard::client::BasicClient; +use jacquard::types::string::AtUri; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Read a Tangled git repository record")] +struct Args { + /// at:// URI of the repo record + /// Example: at://did:plc:xyz/sh.tangled.repo/3lzabc123 + /// The default is the jacquard repository + #[arg(default_value = "at://did:plc:yfvwmnlztr4dwkb7hwz55r2g/sh.tangled.repo/3lzrya6fcwv22")] + uri: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + // Parse the at:// URI + let uri = AtUri::new(&args.uri)?; + + // Create an unauthenticated agent for public record access + let agent = BasicClient::unauthenticated(); + + // Use Agent's get_record helper with the at:// URI + let response = agent.get_record::(uri).await?; + let output = response.into_output()?; + + println!("Tangled Repository\n"); + println!("URI: {}", output.uri); + println!("Name: {}", output.value.name); + + if let Some(desc) = &output.value.description { + println!("Description: {}", desc); + } + + println!("Knot: {}", output.value.knot); + println!("Created: {}", output.value.created_at); + + if let Some(source) = &output.value.source { + println!("Source: {}", source.as_str()); + } + + if let Some(spindle) = &output.value.spindle { + println!("CI Spindle: {}", spindle); + } + + if let Some(labels) = &output.value.labels { + if !labels.is_empty() { + println!( + "Labels available: {}", + labels + .iter() + .map(|l| l.to_string()) + .collect::>() + .join(", ") + ); + } + } + + Ok(()) +} diff --git a/examples/read_whitewind_posts.rs b/examples/read_whitewind_posts.rs new file mode 100644 index 00000000..56be4059 --- /dev/null +++ b/examples/read_whitewind_posts.rs @@ -0,0 +1,40 @@ +use clap::Parser; +use jacquard::api::com_whtwnd::blog::entry::Entry; +use jacquard::client::BasicClient; +use jacquard::types::string::AtUri; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Read a WhiteWind blog post")] +struct Args { + /// at:// URI of the blog entry + /// Example: at://did:plc:xyz/com.whtwnd.blog.entry/3l5abc123 + uri: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + // Parse the at:// URI + let uri = AtUri::new(&args.uri)?; + + // Create an unauthenticated agent for public record access + let agent = BasicClient::unauthenticated(); + + // Use Agent's get_record helper with the at:// URI + let response = agent.get_record::(uri).await?; + let output = response.into_output()?; + + println!("📚 WhiteWind Blog Entry\n"); + println!("URI: {}", output.uri); + println!("Title: {}", output.value.title.as_deref().unwrap_or("[Untitled]")); + if let Some(subtitle) = &output.value.subtitle { + println!("Subtitle: {}", subtitle); + } + if let Some(created) = &output.value.created_at { + println!("Created: {}", created); + } + println!("\n{}", output.value.content); + + Ok(()) +} diff --git a/examples/resolve_did.rs b/examples/resolve_did.rs new file mode 100644 index 00000000..265196b3 --- /dev/null +++ b/examples/resolve_did.rs @@ -0,0 +1,100 @@ +use clap::Parser; +use jacquard::client::BasicClient; +use jacquard::types::string::Handle; +use jacquard_identity::resolver::IdentityResolver; +use miette::IntoDiagnostic; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Resolve a handle to its DID document")] +struct Args { + /// Handle to resolve (e.g., alice.bsky.social) + #[arg(default_value = "pfrazee.com")] + handle: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + // Parse the handle + let handle = Handle::new(&args.handle)?; + + // Create an unauthenticated client with identity resolver + let client = BasicClient::unauthenticated(); + + // Resolve handle to DID + println!("Resolving handle: {}", handle); + let did = client + .resolve_handle(&handle) + .await + .map_err(|e| miette::miette!("Failed to resolve handle: {}", e))?; + + println!("DID: {}\n", did); + + // Resolve DID document + let doc_response = client + .resolve_did_doc(&did) + .await + .map_err(|e| miette::miette!("Failed to resolve DID document: {}", e))?; + + let doc = doc_response + .parse() + .map_err(|e| miette::miette!("Failed to parse DID document: {}", e))?; + + println!("DID Document:"); + println!("ID: {}", doc.id); + + if let Some(aka) = &doc.also_known_as { + if !aka.is_empty() { + println!("\nAlso Known As:"); + for handle in aka { + println!(" - {}", handle); + } + } + } + + if let Some(verification_methods) = &doc.verification_method { + println!("\nVerification Methods:"); + for method in verification_methods { + println!(" ID: {}", method.id); + println!(" Type: {}", method.r#type); + if let Some(controller) = &method.controller { + println!(" Controller: {}", controller); + } + if let Some(key) = &method.public_key_multibase { + println!(" Public Key (multibase): {}", key); + } + if !method.extra_data.is_empty() { + println!(" Extra fields: {:?}", method.extra_data); + } + println!(); + } + } + + if let Some(services) = &doc.service { + println!("Services:"); + for service in services { + println!(" ID: {}", service.id); + println!(" Type: {}", service.r#type); + if let Some(endpoint) = &service.service_endpoint { + println!(" Endpoint: {:?}", endpoint); + } + if !service.extra_data.is_empty() { + println!(" Extra fields: {:?}", service.extra_data); + } + println!(); + } + } + + if !doc.extra_data.is_empty() { + for (key, value) in &doc.extra_data { + println!( + "{}: {}", + key, + serde_json::to_string_pretty(value).into_diagnostic()? + ); + } + } + + Ok(()) +} diff --git a/examples/update_preferences.rs b/examples/update_preferences.rs new file mode 100644 index 00000000..5bdf0445 --- /dev/null +++ b/examples/update_preferences.rs @@ -0,0 +1,95 @@ +use clap::Parser; +use jacquard::api::app_bsky::actor::{AdultContentPref, PreferencesItem}; +use jacquard::client::vec_update::VecUpdate; +use jacquard::client::{Agent, FileAuthStore}; +use jacquard::oauth::atproto::AtprotoClientMetadata; +use jacquard::oauth::client::OAuthClient; +use jacquard::oauth::loopback::LoopbackConfig; +use jacquard::{CowStr, IntoStatic}; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Update Bluesky preferences")] +struct Args { + /// Handle (e.g., alice.bsky.social), DID, or PDS URL + input: CowStr<'static>, + + /// Enable adult content + #[arg(long)] + enable_adult_content: bool, + + /// Path to auth store file (will be created if missing) + #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] + store: String, +} + +/// Helper struct for the VecUpdate pattern on preferences +pub struct PreferencesUpdate; + +impl VecUpdate for PreferencesUpdate { + type GetRequest<'de> = jacquard::api::app_bsky::actor::get_preferences::GetPreferences; + type PutRequest<'de> = jacquard::api::app_bsky::actor::put_preferences::PutPreferences<'de>; + type Item = PreferencesItem<'static>; + + fn build_get<'s>() -> Self::GetRequest<'s> { + jacquard::api::app_bsky::actor::get_preferences::GetPreferences::new().build() + } + + fn build_put<'s>(items: Vec) -> Self::PutRequest<'s> { + jacquard::api::app_bsky::actor::put_preferences::PutPreferences { + preferences: items, + extra_data: Default::default(), + } + } + + fn extract_vec( + output: jacquard::api::app_bsky::actor::get_preferences::GetPreferencesOutput<'_>, + ) -> Vec { + output + .preferences + .into_iter() + .map(|p| p.into_static()) + .collect() + } + + fn matches(a: &Self::Item, b: &Self::Item) -> bool { + // Match by enum variant discriminant + std::mem::discriminant(a) == std::mem::discriminant(b) + } +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); + let session = oauth + .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) + .await?; + + let agent: Agent<_> = Agent::from(session); + + // Create the adult content preference + let adult_pref = AdultContentPref { + enabled: args.enable_adult_content, + extra_data: Default::default(), + }; + + // Update preferences using update_vec_item + // This will replace existing AdultContentPref or add it if not present + agent + .update_vec_item::(PreferencesItem::AdultContentPref(Box::new( + adult_pref, + ))) + .await?; + + println!( + "✓ Updated adult content preference: {}", + if args.enable_adult_content { + "enabled" + } else { + "disabled" + } + ); + + Ok(()) +} diff --git a/examples/update_profile.rs b/examples/update_profile.rs new file mode 100644 index 00000000..a9d55671 --- /dev/null +++ b/examples/update_profile.rs @@ -0,0 +1,67 @@ +use clap::Parser; +use jacquard::CowStr; +use jacquard::api::app_bsky::actor::profile::Profile; +use jacquard::client::{Agent, FileAuthStore}; +use jacquard::oauth::atproto::AtprotoClientMetadata; +use jacquard::oauth::client::OAuthClient; +use jacquard::oauth::loopback::LoopbackConfig; +use jacquard::types::string::AtUri; +use jacquard::xrpc::XrpcClient; +use miette::IntoDiagnostic; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Update profile display name and description")] +struct Args { + /// Handle (e.g., alice.bsky.social), DID, or PDS URL + input: CowStr<'static>, + + /// New display name + #[arg(long)] + display_name: Option, + + /// New bio/description + #[arg(long)] + description: Option, + + /// Path to auth store file (will be created if missing) + #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] + store: String, +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + let args = Args::parse(); + + let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); + let session = oauth + .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) + .await?; + + let agent: Agent<_> = Agent::from(session); + + // Get session info to build the at:// URI for the profile record + let (did, _) = agent + .info() + .await + .ok_or_else(|| miette::miette!("No session info available"))?; + + // Profile records use "self" as the rkey + let uri_string = format!("at://{}/app.bsky.actor.profile/self", did); + let uri = AtUri::new(&uri_string)?; + + // Update profile in-place using the fetch-modify-put pattern + agent + .update_record::(uri, |profile| { + if let Some(name) = &args.display_name { + profile.display_name = Some(CowStr::from(name.clone())); + } + if let Some(desc) = &args.description { + profile.description = Some(CowStr::from(desc.clone())); + } + }) + .await?; + + println!("✓ Profile updated successfully"); + + Ok(()) +} diff --git a/justfile b/justfile index 7284ad8b..edad96c3 100644 --- a/justfile +++ b/justfile @@ -12,3 +12,47 @@ run *ARGS: # Run 'bacon' to run the project (auto-recompiles) watch *ARGS: bacon --job run -- -- {{ ARGS }} + +# Run the OAuth timeline example +example-oauth *ARGS: + cargo run -p jacquard --example oauth_timeline --features fancy -- {{ARGS}} + +# Create a simple post +example-create-post *ARGS: + cargo run -p jacquard --example create_post --features fancy -- {{ARGS}} + +# Create a post with an image +example-post-image *ARGS: + cargo run -p jacquard --example post_with_image --features fancy -- {{ARGS}} + +# Update profile display name and description +example-update-profile *ARGS: + cargo run -p jacquard --example update_profile --features fancy -- {{ARGS}} + +# Fetch public AT Protocol feed (no auth) +example-public-feed: + cargo run -p jacquard --example public_atproto_feed + +# Create a WhiteWind blog post +example-whitewind-create *ARGS: + cargo run -p jacquard --example create_whitewind_post --features fancy -- {{ARGS}} + +# Read a WhiteWind blog post +example-whitewind-read *ARGS: + cargo run -p jacquard --example read_whitewind_posts --features fancy,api_full -- {{ARGS}} + +# Read info about a Tangled git repository +example-tangled-repo *ARGS: + cargo run -p jacquard --example read_tangled_repo --features fancy,api_full -- {{ARGS}} + +# Resolve a handle to its DID document +example-resolve-did *ARGS: + cargo run -p jacquard --example resolve_did -- {{ARGS}} + +# Update Bluesky preferences +example-update-preferences *ARGS: + cargo run -p jacquard --example update_preferences --features fancy -- {{ARGS}} + +# Run the Axum server example +example-axum: + cargo run -p jacquard-axum --example axum_server --features jacquard/fancy