diff --git a/atrium-api/src/lib.rs b/atrium-api/src/lib.rs index e8177efa..b301d29a 100644 --- a/atrium-api/src/lib.rs +++ b/atrium-api/src/lib.rs @@ -10,5 +10,6 @@ pub mod client; pub mod com; pub mod did_doc; pub mod error; +pub mod record; pub mod tools; pub mod types; diff --git a/atrium-api/src/record.rs b/atrium-api/src/record.rs new file mode 100644 index 00000000..ec471cec --- /dev/null +++ b/atrium-api/src/record.rs @@ -0,0 +1,258 @@ +// This file is generated by atrium-codegen. DO NOT EDIT. +//!A collection of known record types. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(tag = "$type")] +pub enum KnownRecord { + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.actor.profile")] + AppBskyActorProfile(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.feed.generator")] + AppBskyFeedGenerator(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.feed.like")] + AppBskyFeedLike(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.feed.post")] + AppBskyFeedPost(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.feed.repost")] + AppBskyFeedRepost(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.feed.threadgate")] + AppBskyFeedThreadgate(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.block")] + AppBskyGraphBlock(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.follow")] + AppBskyGraphFollow(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.list")] + AppBskyGraphList(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.listblock")] + AppBskyGraphListblock(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.listitem")] + AppBskyGraphListitem(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.graph.starterpack")] + AppBskyGraphStarterpack(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] + #[serde(rename = "app.bsky.labeler.service")] + AppBskyLabelerService(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] + #[cfg(feature = "namespace-chatbsky")] + #[serde(rename = "chat.bsky.actor.declaration")] + ChatBskyActorDeclaration(Box), +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::actor::profile::Record) -> Self { + KnownRecord::AppBskyActorProfile(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::actor::profile::RecordData) -> Self { + KnownRecord::AppBskyActorProfile(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::feed::generator::Record) -> Self { + KnownRecord::AppBskyFeedGenerator(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::feed::generator::RecordData) -> Self { + KnownRecord::AppBskyFeedGenerator(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::feed::like::Record) -> Self { + KnownRecord::AppBskyFeedLike(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::feed::like::RecordData) -> Self { + KnownRecord::AppBskyFeedLike(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::feed::post::Record) -> Self { + KnownRecord::AppBskyFeedPost(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::feed::post::RecordData) -> Self { + KnownRecord::AppBskyFeedPost(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::feed::repost::Record) -> Self { + KnownRecord::AppBskyFeedRepost(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::feed::repost::RecordData) -> Self { + KnownRecord::AppBskyFeedRepost(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::feed::threadgate::Record) -> Self { + KnownRecord::AppBskyFeedThreadgate(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::feed::threadgate::RecordData) -> Self { + KnownRecord::AppBskyFeedThreadgate(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::block::Record) -> Self { + KnownRecord::AppBskyGraphBlock(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::block::RecordData) -> Self { + KnownRecord::AppBskyGraphBlock(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::follow::Record) -> Self { + KnownRecord::AppBskyGraphFollow(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::follow::RecordData) -> Self { + KnownRecord::AppBskyGraphFollow(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::list::Record) -> Self { + KnownRecord::AppBskyGraphList(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::list::RecordData) -> Self { + KnownRecord::AppBskyGraphList(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::listblock::Record) -> Self { + KnownRecord::AppBskyGraphListblock(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::listblock::RecordData) -> Self { + KnownRecord::AppBskyGraphListblock(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::listitem::Record) -> Self { + KnownRecord::AppBskyGraphListitem(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::listitem::RecordData) -> Self { + KnownRecord::AppBskyGraphListitem(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::graph::starterpack::Record) -> Self { + KnownRecord::AppBskyGraphStarterpack(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::graph::starterpack::RecordData) -> Self { + KnownRecord::AppBskyGraphStarterpack(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record: crate::app::bsky::labeler::service::Record) -> Self { + KnownRecord::AppBskyLabelerService(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] +impl From for KnownRecord { + fn from(record_data: crate::app::bsky::labeler::service::RecordData) -> Self { + KnownRecord::AppBskyLabelerService(Box::new(record_data.into())) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] +#[cfg(feature = "namespace-chatbsky")] +impl From for KnownRecord { + fn from(record: crate::chat::bsky::actor::declaration::Record) -> Self { + KnownRecord::ChatBskyActorDeclaration(Box::new(record)) + } +} +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] +#[cfg(feature = "namespace-chatbsky")] +impl From for KnownRecord { + fn from(record_data: crate::chat::bsky::actor::declaration::RecordData) -> Self { + KnownRecord::ChatBskyActorDeclaration(Box::new(record_data.into())) + } +} diff --git a/bsky-sdk/src/record.rs b/bsky-sdk/src/record.rs index ce5d85a3..ac44d976 100644 --- a/bsky-sdk/src/record.rs +++ b/bsky-sdk/src/record.rs @@ -1,7 +1,6 @@ //! Record operations. mod agent; -pub use self::agent::*; use crate::error::{Error, Result}; use crate::BskyAgent; use async_trait::async_trait; diff --git a/bsky-sdk/src/record/agent.rs b/bsky-sdk/src/record/agent.rs index 1cd75765..2f8a4506 100644 --- a/bsky-sdk/src/record/agent.rs +++ b/bsky-sdk/src/record/agent.rs @@ -3,120 +3,17 @@ use crate::error::{Error, Result}; use crate::BskyAgent; use atrium_api::agent::store::SessionStore; use atrium_api::com::atproto::repo::create_record; +use atrium_api::record::KnownRecord; use atrium_api::types::string::RecordKey; use atrium_api::xrpc::XrpcClient; -pub enum CreateRecordSubject { - AppBskyActorProfile(Box), - AppBskyFeedGenerator(Box), - AppBskyFeedLike(Box), - AppBskyFeedPost(Box), - AppBskyFeedRepost(Box), - AppBskyFeedThreadgate(Box), - AppBskyGraphBlock(Box), - AppBskyGraphFollow(Box), - AppBskyGraphList(Box), - AppBskyGraphListblock(Box), - AppBskyGraphListitem(Box), - AppBskyGraphStarterpack(Box), - AppBskyLabelerService(Box), - ChatBskyActorDeclaration(Box), -} - -macro_rules! into_create_record_subject { - ($record:path, $record_data:path, $variant:ident) => { - impl From<$record> for CreateRecordSubject { - fn from(record: $record) -> Self { - Self::$variant(Box::new(record)) - } - } - - impl From<$record_data> for CreateRecordSubject { - fn from(record_data: $record_data) -> Self { - Self::$variant(Box::new(record_data.into())) - } - } - }; -} - -into_create_record_subject!( - atrium_api::app::bsky::actor::profile::Record, - atrium_api::app::bsky::actor::profile::RecordData, - AppBskyActorProfile -); -into_create_record_subject!( - atrium_api::app::bsky::feed::generator::Record, - atrium_api::app::bsky::feed::generator::RecordData, - AppBskyFeedGenerator -); -into_create_record_subject!( - atrium_api::app::bsky::feed::like::Record, - atrium_api::app::bsky::feed::like::RecordData, - AppBskyFeedLike -); -into_create_record_subject!( - atrium_api::app::bsky::feed::post::Record, - atrium_api::app::bsky::feed::post::RecordData, - AppBskyFeedPost -); -into_create_record_subject!( - atrium_api::app::bsky::feed::repost::Record, - atrium_api::app::bsky::feed::repost::RecordData, - AppBskyFeedRepost -); -into_create_record_subject!( - atrium_api::app::bsky::feed::threadgate::Record, - atrium_api::app::bsky::feed::threadgate::RecordData, - AppBskyFeedThreadgate -); -into_create_record_subject!( - atrium_api::app::bsky::graph::block::Record, - atrium_api::app::bsky::graph::block::RecordData, - AppBskyGraphBlock -); -into_create_record_subject!( - atrium_api::app::bsky::graph::follow::Record, - atrium_api::app::bsky::graph::follow::RecordData, - AppBskyGraphFollow -); -into_create_record_subject!( - atrium_api::app::bsky::graph::list::Record, - atrium_api::app::bsky::graph::list::RecordData, - AppBskyGraphList -); -into_create_record_subject!( - atrium_api::app::bsky::graph::listblock::Record, - atrium_api::app::bsky::graph::listblock::RecordData, - AppBskyGraphListblock -); -into_create_record_subject!( - atrium_api::app::bsky::graph::listitem::Record, - atrium_api::app::bsky::graph::listitem::RecordData, - AppBskyGraphListitem -); -into_create_record_subject!( - atrium_api::app::bsky::graph::starterpack::Record, - atrium_api::app::bsky::graph::starterpack::RecordData, - AppBskyGraphStarterpack -); -into_create_record_subject!( - atrium_api::app::bsky::labeler::service::Record, - atrium_api::app::bsky::labeler::service::RecordData, - AppBskyLabelerService -); -into_create_record_subject!( - atrium_api::chat::bsky::actor::declaration::Record, - atrium_api::chat::bsky::actor::declaration::RecordData, - ChatBskyActorDeclaration -); - impl BskyAgent where T: XrpcClient + Send + Sync, S: SessionStore + Send + Sync, { /// Create a record with various types of data. - /// For example, the Record families defined in [`KnownRecord`](atrium_api::records::KnownRecord) are supported. + /// For example, the Record families defined in [`KnownRecord`](atrium_api::record::KnownRecord) are supported. /// /// # Example /// @@ -135,23 +32,23 @@ where /// ``` pub async fn create_record( &self, - subject: impl Into, + subject: impl Into, ) -> Result { match subject.into() { - CreateRecordSubject::AppBskyActorProfile(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyFeedGenerator(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyFeedLike(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyFeedPost(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyFeedRepost(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyFeedThreadgate(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphBlock(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphFollow(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphList(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphListblock(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphListitem(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyGraphStarterpack(record) => record.data.create(self).await, - CreateRecordSubject::AppBskyLabelerService(record) => record.data.create(self).await, - CreateRecordSubject::ChatBskyActorDeclaration(record) => record.data.create(self).await, + KnownRecord::AppBskyActorProfile(record) => record.data.create(self).await, + KnownRecord::AppBskyFeedGenerator(record) => record.data.create(self).await, + KnownRecord::AppBskyFeedLike(record) => record.data.create(self).await, + KnownRecord::AppBskyFeedPost(record) => record.data.create(self).await, + KnownRecord::AppBskyFeedRepost(record) => record.data.create(self).await, + KnownRecord::AppBskyFeedThreadgate(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphBlock(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphFollow(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphList(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphListblock(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphListitem(record) => record.data.create(self).await, + KnownRecord::AppBskyGraphStarterpack(record) => record.data.create(self).await, + KnownRecord::AppBskyLabelerService(record) => record.data.create(self).await, + KnownRecord::ChatBskyActorDeclaration(record) => record.data.create(self).await, } } /// Delete a record with AT URI. diff --git a/lexicon/atrium-codegen/src/generator.rs b/lexicon/atrium-codegen/src/generator.rs index edbaffa1..a8f67685 100644 --- a/lexicon/atrium-codegen/src/generator.rs +++ b/lexicon/atrium-codegen/src/generator.rs @@ -1,6 +1,8 @@ use crate::fs::find_dirs; use crate::schema::find_ref_unions; -use crate::token_stream::{client, collection, modules, record_enum, ref_unions, user_type}; +use crate::token_stream::{ + client, collection, enum_common, impl_into_record, modules, ref_unions, user_type, +}; use atrium_lex::lexicon::LexUserType; use atrium_lex::LexiconDoc; use heck::ToSnakeCase; @@ -94,18 +96,14 @@ pub(crate) fn generate_records( }) .sorted() .collect_vec(); - let tokens = record_enum(&records, "KnownRecord", None, namespaces)?; + let known_record = enum_common(&records, "KnownRecord", None, namespaces)?; + let impl_into = impl_into_record(&records, namespaces)?; let content = quote! { - #![doc = "A collection of ATP repository record types."] - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] - #[serde(untagged)] - pub enum Record { - Known(KnownRecord), - Unknown(crate::types::UnknownData), - } - #tokens + #![doc = "A collection of known record types."] + #known_record + #impl_into }; - let path = outdir.join("records.rs"); + let path = outdir.join("record.rs"); write_to_file(File::create(&path)?, content)?; Ok(path) } diff --git a/lexicon/atrium-codegen/src/token_stream.rs b/lexicon/atrium-codegen/src/token_stream.rs index d4c5df3e..a0277b22 100644 --- a/lexicon/atrium-codegen/src/token_stream.rs +++ b/lexicon/atrium-codegen/src/token_stream.rs @@ -577,10 +577,10 @@ fn description(description: &Option) -> TokenStream { } fn refs_enum(refs: &[String], name: &str, schema_id: Option<&str>) -> Result { - record_enum(refs, name, schema_id, &[]) + enum_common(refs, name, schema_id, &[]) } -pub fn record_enum( +pub fn enum_common( refs: &[String], name: &str, schema_id: Option<&str>, @@ -639,6 +639,52 @@ pub fn record_enum( }) } +pub fn impl_into_record( + refs: &[String], + namespaces: &[(&str, Option<&str>)], +) -> Result { + let mut impls = Vec::new(); + for r#ref in refs { + let record_path = resolve_path(r#ref, "record")?; + let record_data_path = resolve_path(r#ref, "record_data")?; + let s = record_path.to_string().replace(' ', ""); + let mut parts = s + .strip_prefix("crate::") + .unwrap_or(&s) + .split("::") + .map(str::to_pascal_case) + .collect_vec(); + parts.pop(); + let name = format_ident!("{}", parts.join("")); + let mut feature = quote!(); + if let Some((_, Some(feature_name))) = namespaces + .iter() + .find(|(prefix, _)| r#ref.starts_with(prefix)) + { + feature = quote! { + #[cfg_attr(docsrs, doc(cfg(feature = #feature_name)))] + #[cfg(feature = #feature_name)] + }; + } + impls.push(quote! { + #feature + impl From<#record_path> for KnownRecord { + fn from(record: #record_path) -> Self { + KnownRecord::#name(Box::new(record)) + } + } + + #feature + impl From<#record_data_path> for KnownRecord { + fn from(record_data: #record_data_path) -> Self { + KnownRecord::#name(Box::new(record_data.into())) + } + } + }); + } + Ok(quote!(#(#impls)*)) +} + pub fn modules( names: &[String], components: &[&str],