From ccc0213d91651f0aa5783d0cff1537d8e3e83985 Mon Sep 17 00:00:00 2001 From: Yoshihiro Sugi Date: Mon, 20 May 2024 09:08:44 +0900 Subject: [PATCH] feat(api): Add namespace features (#174) * Update codegen: add namespace features * Update API, generated by new codegen * Update dependencies * Update workflows * Update README --- .github/workflows/api.yml | 11 +- Cargo.lock | 4 +- Cargo.toml | 2 +- atrium-api/Cargo.toml | 6 +- atrium-api/README.md | 20 ++- atrium-api/src/app.rs | 2 + atrium-api/src/chat.rs | 2 + atrium-api/src/client.rs | 189 ++++++++++++++++++--- atrium-api/src/records.rs | 26 +++ atrium-api/src/tools.rs | 2 + lexicon/atrium-codegen/src/generator.rs | 33 ++-- lexicon/atrium-codegen/src/lib.rs | 10 +- lexicon/atrium-codegen/src/token_stream.rs | 90 +++++++++- lexicon/lexgen/src/bin/main.rs | 7 +- 14 files changed, 343 insertions(+), 61 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index e9660515..569aa42f 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -15,9 +15,16 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build - run: cargo build -p atrium-api --verbose + run: | + cargo build -p atrium-api --verbose + cargo build -p atrium-api --verbose --no-default-features + cargo build -p atrium-api --verbose --no-default-features --features agent + cargo build -p atrium-api --verbose --no-default-features --features bluesky + cargo build -p atrium-api --verbose --all-features - name: Run tests run: | - cargo test -p atrium-api --lib + cargo test -p atrium-api cargo test -p atrium-api --lib --no-default-features + cargo test -p atrium-api --lib --no-default-features --features agent + cargo test -p atrium-api --lib --no-default-features --features bluesky cargo test -p atrium-api --lib --all-features diff --git a/Cargo.lock b/Cargo.lock index 7ff143fb..fce8fe00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1817,9 +1817,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 5c6cb93a..efee2971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ serde_html_form = "0.2.6" # Networking futures = { version = "0.3.30", default-features = false, features = ["alloc"] } http = "1.1.0" -tokio = { version = "1.36", default-features = false } +tokio = { version = "1.37", default-features = false } # HTTP client integrations isahc = "1.7.2" diff --git a/atrium-api/Cargo.toml b/atrium-api/Cargo.toml index 3bcf2134..8f0a45ef 100644 --- a/atrium-api/Cargo.toml +++ b/atrium-api/Cargo.toml @@ -24,8 +24,12 @@ serde_bytes.workspace = true tokio = { workspace = true, optional = true } [features] -default = ["agent"] +default = ["agent", "bluesky"] agent = ["tokio/sync"] +bluesky = ["namespace-appbsky", "namespace-chatbsky", "namespace-toolsozone"] +namespace-appbsky = [] +namespace-chatbsky = [] +namespace-toolsozone = [] [dev-dependencies] atrium-xrpc-client.workspace = true diff --git a/atrium-api/README.md b/atrium-api/README.md index 5406b5e6..8a079459 100644 --- a/atrium-api/README.md +++ b/atrium-api/README.md @@ -36,7 +36,7 @@ async fn main() -> Result<(), Box> { } ``` -### `AtpAgent` +### `AtpAgent` (`agent` feature) While `AtpServiceClient` can be used for simple XRPC calls, it is better to use `AtpAgent`, which has practical features such as session management. @@ -53,14 +53,20 @@ async fn main() -> Result<(), Box> { agent.login("alice@mail.com", "hunter2").await?; let result = agent .api - .app - .bsky - .actor - .get_profile(atrium_api::app::bsky::actor::get_profile::Parameters { - actor: "bsky.app".parse()?, - }) + .com + .atproto + .server + .get_session() .await?; println!("{:?}", result); Ok(()) } ``` + +## Features + +The `AtpAgent` used in the above example is included in the `agent` feature. atrium-api enables the `agent` and `bluesky` features by default. It is possible to opt-out if not needed. + +- `agent`: enable the `agent` module. +- `bluesky`: enable bluesky-specific lexicon definitions and XRPC methods. + - It is also possible to enable only the namespace specified by `namespace-*`. diff --git a/atrium-api/src/app.rs b/atrium-api/src/app.rs index 9055a144..8854e101 100644 --- a/atrium-api/src/app.rs +++ b/atrium-api/src/app.rs @@ -1,3 +1,5 @@ // This file is generated by atrium-codegen. DO NOT EDIT. //!Definitions for the `app` namespace. +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] +#[cfg(feature = "namespace-appbsky")] pub mod bsky; diff --git a/atrium-api/src/chat.rs b/atrium-api/src/chat.rs index b4c5b55e..4f52b212 100644 --- a/atrium-api/src/chat.rs +++ b/atrium-api/src/chat.rs @@ -1,3 +1,5 @@ // This file is generated by atrium-codegen. DO NOT EDIT. //!Definitions for the `chat` namespace. +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] +#[cfg(feature = "namespace-chatbsky")] pub mod bsky; diff --git a/atrium-api/src/client.rs b/atrium-api/src/client.rs index ea003262..8b8bc69d 100644 --- a/atrium-api/src/client.rs +++ b/atrium-api/src/client.rs @@ -25,14 +25,20 @@ where pub chat: chat::Service, pub com: com::Service, pub tools: tools::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod app { pub struct Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] pub bsky: bsky::Service, + pub(crate) _phantom: core::marker::PhantomData, } + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-appbsky")))] + #[cfg(feature = "namespace-appbsky")] pub mod bsky { pub struct Service where @@ -44,6 +50,7 @@ pub mod app { pub labeler: labeler::Service, pub notification: notification::Service, pub unspecced: unspecced::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod actor { pub struct Service @@ -51,6 +58,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod feed { @@ -59,6 +67,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod graph { @@ -67,6 +76,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod labeler { @@ -75,6 +85,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod notification { @@ -83,6 +94,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod unspecced { @@ -91,6 +103,7 @@ pub mod app { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } } @@ -100,8 +113,13 @@ pub mod chat { where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] + #[cfg(feature = "namespace-chatbsky")] pub bsky: bsky::Service, + pub(crate) _phantom: core::marker::PhantomData, } + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] + #[cfg(feature = "namespace-chatbsky")] pub mod bsky { pub struct Service where @@ -110,6 +128,7 @@ pub mod chat { pub actor: actor::Service, pub convo: convo::Service, pub moderation: moderation::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod actor { pub struct Service @@ -117,6 +136,7 @@ pub mod chat { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod convo { @@ -125,6 +145,7 @@ pub mod chat { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod moderation { @@ -133,6 +154,7 @@ pub mod chat { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } } @@ -143,6 +165,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub atproto: atproto::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod atproto { pub struct Service @@ -157,6 +180,7 @@ pub mod com { pub server: server::Service, pub sync: sync::Service, pub temp: temp::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod admin { pub struct Service @@ -164,6 +188,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod identity { @@ -172,6 +197,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod label { @@ -180,6 +206,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod moderation { @@ -188,6 +215,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod repo { @@ -196,6 +224,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod server { @@ -204,6 +233,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod sync { @@ -212,6 +242,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod temp { @@ -220,6 +251,7 @@ pub mod com { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } } @@ -229,8 +261,13 @@ pub mod tools { where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-toolsozone")))] + #[cfg(feature = "namespace-toolsozone")] pub ozone: ozone::Service, + pub(crate) _phantom: core::marker::PhantomData, } + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-toolsozone")))] + #[cfg(feature = "namespace-toolsozone")] pub mod ozone { pub struct Service where @@ -238,6 +275,7 @@ pub mod tools { { pub communication: communication::Service, pub moderation: moderation::Service, + pub(crate) _phantom: core::marker::PhantomData, } pub mod communication { pub struct Service @@ -245,6 +283,7 @@ pub mod tools { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } pub mod moderation { @@ -253,6 +292,7 @@ pub mod tools { T: atrium_xrpc::XrpcClient + Send + Sync, { pub(crate) xrpc: std::sync::Arc, + pub(crate) _phantom: core::marker::PhantomData, } } } @@ -261,12 +301,14 @@ impl self::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { app: app::Service::new(std::sync::Arc::clone(&xrpc)), chat: chat::Service::new(std::sync::Arc::clone(&xrpc)), com: com::Service::new(std::sync::Arc::clone(&xrpc)), tools: tools::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } @@ -274,16 +316,21 @@ impl app::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { + #[cfg(feature = "namespace-appbsky")] bsky: app::bsky::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { actor: app::bsky::actor::Service::new(std::sync::Arc::clone(&xrpc)), @@ -294,15 +341,21 @@ where std::sync::Arc::clone(&xrpc), ), unspecced: app::bsky::unspecced::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::actor::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Get private preferences attached to the current account. Expected use is synchronization between multiple devices, and import/export during account migration. Requires auth. pub async fn get_preferences( @@ -512,12 +565,17 @@ where } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::feed::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Get information about a feed generator, including policies and offered feed URIs. Does not require auth; implemented by Feed Generator services (not App View). pub async fn describe_feed_generator( @@ -1029,12 +1087,17 @@ where } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::graph::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Enumerates which accounts the requesting account is currently blocking. Requires auth. pub async fn get_blocks( @@ -1445,12 +1508,17 @@ where } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::labeler::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Get information about a list of labeler services. pub async fn get_services( @@ -1483,12 +1551,17 @@ where } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::notification::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Count the number of unread notifications for the requesting account. Requires auth. pub async fn get_unread_count( @@ -1605,12 +1678,17 @@ where } } } +#[cfg(feature = "namespace-appbsky")] impl app::bsky::unspecced::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///An unspecced view of globally popular feed generators. pub async fn get_popular_feed_generators( @@ -1767,16 +1845,21 @@ impl chat::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { + #[cfg(feature = "namespace-chatbsky")] bsky: chat::bsky::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-chatbsky")] impl chat::bsky::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { actor: chat::bsky::actor::Service::new(std::sync::Arc::clone(&xrpc)), @@ -1784,15 +1867,21 @@ where moderation: chat::bsky::moderation::Service::new( std::sync::Arc::clone(&xrpc), ), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-chatbsky")] impl chat::bsky::actor::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } pub async fn delete_account( &self, @@ -1851,12 +1940,17 @@ where } } } +#[cfg(feature = "namespace-chatbsky")] impl chat::bsky::convo::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } pub async fn delete_message_for_self( &self, @@ -2207,12 +2301,17 @@ where } } } +#[cfg(feature = "namespace-chatbsky")] impl chat::bsky::moderation::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } pub async fn get_actor_metadata( &self, @@ -2306,9 +2405,11 @@ impl com::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { atproto: com::atproto::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } @@ -2316,6 +2417,7 @@ impl com::atproto::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { admin: com::atproto::admin::Service::new(std::sync::Arc::clone(&xrpc)), @@ -2328,6 +2430,7 @@ where server: com::atproto::server::Service::new(std::sync::Arc::clone(&xrpc)), sync: com::atproto::sync::Service::new(std::sync::Arc::clone(&xrpc)), temp: com::atproto::temp::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } @@ -2335,8 +2438,12 @@ impl com::atproto::admin::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Delete a user account as an administrator. pub async fn delete_account( @@ -2730,8 +2837,12 @@ impl com::atproto::identity::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Describe the credentials that should be included in the DID doc of an account that is migrating to this service. pub async fn get_recommended_did_credentials( @@ -2913,8 +3024,12 @@ impl com::atproto::label::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Find labels relevant to the provided AT-URI patterns. Public endpoint for moderation services, though may return different or additional results with auth. pub async fn query_labels( @@ -2951,8 +3066,12 @@ impl com::atproto::moderation::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Submit a moderation report regarding an atproto account or record. Implemented by moderation services (with PDS proxying), and requires auth. pub async fn create_report( @@ -2989,8 +3108,12 @@ impl com::atproto::repo::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS. pub async fn apply_writes( @@ -3288,8 +3411,12 @@ impl com::atproto::server::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Activates a currently deactivated account. Used to finalize account migration after the account's repo is imported and identity is setup. pub async fn activate_account( @@ -4018,8 +4145,12 @@ impl com::atproto::sync::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Get a blob associated with a given account. Returns the full blob as originally uploaded. Does not require auth; implemented by PDS. pub async fn get_blob( @@ -4335,8 +4466,12 @@ impl com::atproto::temp::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Check accounts location in signup queue. pub async fn check_signup_queue( @@ -4432,16 +4567,21 @@ impl tools::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { + #[cfg(feature = "namespace-toolsozone")] ozone: tools::ozone::Service::new(std::sync::Arc::clone(&xrpc)), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-toolsozone")] impl tools::ozone::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { communication: tools::ozone::communication::Service::new( @@ -4450,15 +4590,21 @@ where moderation: tools::ozone::moderation::Service::new( std::sync::Arc::clone(&xrpc), ), + _phantom: core::marker::PhantomData, } } } +#[cfg(feature = "namespace-toolsozone")] impl tools::ozone::communication::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Administrative action to create a new, re-usable communication (email for now) template. pub async fn create_template( @@ -4580,12 +4726,17 @@ where } } } +#[cfg(feature = "namespace-toolsozone")] impl tools::ozone::moderation::Service where T: atrium_xrpc::XrpcClient + Send + Sync, { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { - Self { xrpc } + Self { + xrpc, + _phantom: core::marker::PhantomData, + } } ///Take a moderation action on an actor. pub async fn emit_event( diff --git a/atrium-api/src/records.rs b/atrium-api/src/records.rs index 6ca6baa0..e297ee01 100644 --- a/atrium-api/src/records.rs +++ b/atrium-api/src/records.rs @@ -9,30 +9,56 @@ pub enum Record { #[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.labeler.service")] AppBskyLabelerService(Box), + #[cfg_attr(docsrs, doc(cfg(feature = "namespace-chatbsky")))] + #[cfg(feature = "namespace-chatbsky")] #[serde(rename = "chat.bsky.actor.declaration")] ChatBskyActorDeclaration(Box), } diff --git a/atrium-api/src/tools.rs b/atrium-api/src/tools.rs index 71238949..17a2adb1 100644 --- a/atrium-api/src/tools.rs +++ b/atrium-api/src/tools.rs @@ -1,3 +1,5 @@ // This file is generated by atrium-codegen. DO NOT EDIT. //!Definitions for the `tools` namespace. +#[cfg_attr(docsrs, doc(cfg(feature = "namespace-toolsozone")))] +#[cfg(feature = "namespace-toolsozone")] pub mod ozone; diff --git a/lexicon/atrium-codegen/src/generator.rs b/lexicon/atrium-codegen/src/generator.rs index be6407e9..9b4afa6c 100644 --- a/lexicon/atrium-codegen/src/generator.rs +++ b/lexicon/atrium-codegen/src/generator.rs @@ -1,6 +1,6 @@ use crate::fs::find_dirs; use crate::schema::find_ref_unions; -use crate::token_stream::{client, collection, modules, ref_unions, refs_enum, user_type}; +use crate::token_stream::{client, collection, modules, record_enum, ref_unions, user_type}; use atrium_lex::lexicon::LexUserType; use atrium_lex::LexiconDoc; use heck::ToSnakeCase; @@ -76,6 +76,7 @@ pub(crate) fn generate_schemas( pub(crate) fn generate_records( outdir: &Path, schemas: &[LexiconDoc], + namespaces: &[(&str, Option<&str>)], ) -> Result> { let records = schemas .iter() @@ -88,7 +89,7 @@ pub(crate) fn generate_records( }) .sorted() .collect_vec(); - let tokens = refs_enum(&records, "KnownRecord", None)?; + let tokens = record_enum(&records, "KnownRecord", None, namespaces)?; let content = quote! { #![doc = "A collection of ATP repository record types."] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] @@ -107,6 +108,7 @@ pub(crate) fn generate_records( pub(crate) fn generate_client( outdir: &Path, schemas: &[LexiconDoc], + namespaces: &[(&str, Option<&str>)], ) -> Result> { let mut schema_map = HashMap::new(); let mut tree = HashMap::new(); @@ -129,7 +131,7 @@ pub(crate) fn generate_client( } } } - let tokens = client(&tree, &schema_map)?; + let tokens = client(&tree, &schema_map, namespaces)?; let content = quote! { #![doc = r#"Structs for ATP client, implements all HTTP APIs of XRPC."#] #tokens @@ -142,6 +144,7 @@ pub(crate) fn generate_client( pub(crate) fn generate_modules( outdir: &Path, schemas: &[LexiconDoc], + namespaces: &[(&str, Option<&str>)], ) -> Result, Box> { let mut paths = find_dirs(outdir)?; paths.reverse(); @@ -171,19 +174,18 @@ pub(crate) fn generate_modules( }) .sorted() .collect_vec(); - let modules = modules(&names)?; - let (documentation, collections) = if path.as_ref() == outdir { - ( - quote! { - #![doc = include_str!("../README.md")] - pub use atrium_xrpc as xrpc; - }, - vec![], - ) - } else if let Ok(relative) = path.as_ref().strip_prefix(outdir) { + let relative = path.as_ref().strip_prefix(outdir)?; + let modules = modules( + &names, + &relative + .components() + .filter_map(|c| c.as_os_str().to_str()) + .collect_vec(), + namespaces, + )?; + let (documentation, collections) = { let ns = relative.to_string_lossy().replace(['/', '\\'], "."); let doc = format!("Definitions for the `{}` namespace.", ns); - let collections = names .iter() .filter_map(|name| { @@ -202,10 +204,7 @@ pub(crate) fn generate_modules( .map(|_| collection(name, &nsid)) }) .collect_vec(); - (quote!(#![doc = #doc]), collections) - } else { - (quote!(), vec![]) }; let content = quote! { #documentation diff --git a/lexicon/atrium-codegen/src/lib.rs b/lexicon/atrium-codegen/src/lib.rs index 17671821..c1c60b24 100644 --- a/lexicon/atrium-codegen/src/lib.rs +++ b/lexicon/atrium-codegen/src/lib.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; pub fn genapi( lexdir: impl AsRef, outdir: impl AsRef, - prefixes: &[&str], + namespaces: &[(&str, Option<&str>)], ) -> Result>, Box> { let lexdir = lexdir.as_ref().canonicalize()?; let outdir = outdir.as_ref().canonicalize()?; @@ -24,16 +24,16 @@ pub fn genapi( schemas.push(from_reader::<_, LexiconDoc>(File::open(path)?)?); } let mut results = Vec::new(); - for &prefix in prefixes { + for &(prefix, _) in namespaces { let targets = schemas .iter() .filter(|schema| schema.id.starts_with(prefix)) .collect_vec(); results.extend(gen(&outdir, &targets)?); } - results.push(generate_records(&outdir, &schemas)?); - results.push(generate_client(&outdir, &schemas)?); - results.extend(generate_modules(&outdir, &schemas)?); + results.push(generate_records(&outdir, &schemas, namespaces)?); + results.push(generate_client(&outdir, &schemas, namespaces)?); + results.extend(generate_modules(&outdir, &schemas, namespaces)?); Ok(results) } diff --git a/lexicon/atrium-codegen/src/token_stream.rs b/lexicon/atrium-codegen/src/token_stream.rs index 598fd4e8..8b85fe47 100644 --- a/lexicon/atrium-codegen/src/token_stream.rs +++ b/lexicon/atrium-codegen/src/token_stream.rs @@ -580,7 +580,16 @@ fn description(description: &Option) -> TokenStream { } } -pub fn refs_enum(refs: &[String], name: &str, schema_id: Option<&str>) -> Result { +fn refs_enum(refs: &[String], name: &str, schema_id: Option<&str>) -> Result { + record_enum(refs, name, schema_id, &[]) +} + +pub fn record_enum( + refs: &[String], + name: &str, + schema_id: Option<&str>, + namespaces: &[(&str, Option<&str>)], +) -> Result { let is_record = schema_id.is_none(); let derives = derives()?; let enum_name = format_ident!("{name}"); @@ -608,7 +617,20 @@ pub fn refs_enum(refs: &[String], name: &str, schema_id: Option<&str>) -> Result parts.pop(); } let name = format_ident!("{}", parts.join("")); + let mut feature = quote!(); + if is_record { + 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)] + }; + } + } variants.push(quote! { + #feature #[serde(rename = #rename)] #name(Box<#path>) }); @@ -622,12 +644,28 @@ pub fn refs_enum(refs: &[String], name: &str, schema_id: Option<&str>) -> Result }) } -pub fn modules(names: &[String]) -> Result { +pub fn modules( + names: &[String], + components: &[&str], + namespaces: &[(&str, Option<&str>)], +) -> Result { let v = names .iter() .map(|s| { + let namespace = components.iter().chain(&[s.as_str()]).join("."); + let feature = if let Some((_, Some(feature_name))) = + namespaces.iter().find(|(prefix, _)| &namespace == prefix) + { + quote! { + #[cfg_attr(docsrs, doc(cfg(feature = #feature_name)))] + #[cfg(feature = #feature_name)] + } + } else { + quote!() + }; let m = format_ident!("{s}"); quote! { + #feature pub mod #m; } }) @@ -638,8 +676,9 @@ pub fn modules(names: &[String]) -> Result { pub fn client( tree: &HashMap>, schemas: &HashMap, + namespaces: &[(&str, Option<&str>)], ) -> Result { - let services = client_services("", tree)?; + let services = client_services("", tree, namespaces)?; let mut impls = Vec::new(); for key in tree.keys().sorted() { let type_name = if key.is_empty() { @@ -648,7 +687,7 @@ pub fn client( let path = syn::parse_str::(&key.split('.').join("::"))?; quote!(#path::Service) }; - let fn_new = client_new(tree, key)?; + let fn_new = client_new(tree, key, namespaces)?; let mut methods = Vec::new(); for (name, _) in tree[key].iter().filter(|(_, b)| *b).sorted() { let nsid = format!("{key}.{name}"); @@ -659,7 +698,16 @@ pub fn client( }; methods.push(method); } + let feature = if let Some((_, Some(feature_name))) = namespaces + .iter() + .find(|(prefix, _)| key.starts_with(prefix)) + { + quote!(#[cfg(feature = #feature_name)]) + } else { + quote!() + }; impls.push(quote! { + #feature impl #type_name where T: atrium_xrpc::XrpcClient + Send + Sync, @@ -695,6 +743,7 @@ pub fn client( fn client_services( target: &str, tree: &HashMap>, + namespaces: &[(&str, Option<&str>)], ) -> Result { let mut fields = Vec::new(); let mut mods = Vec::new(); @@ -705,7 +754,19 @@ fn client_services( has_leaf = true; } else { let name = format_ident!("{child}"); + let namespace = format!("{target}.{child}"); + let feature = if let Some((_, Some(feature_name))) = + namespaces.iter().find(|(prefix, _)| prefix == &namespace) + { + quote! { + #[cfg_attr(docsrs, doc(cfg(feature = #feature_name)))] + #[cfg(feature = #feature_name)] + } + } else { + quote!() + }; fields.push(quote! { + #feature pub #name: #name::Service, }); let target = if target.is_empty() { @@ -713,8 +774,9 @@ fn client_services( } else { format!("{target}.{child}") }; - let submodule = client_services(&target, tree)?; + let submodule = client_services(&target, tree, namespaces)?; mods.push(quote! { + #feature pub mod #name { #submodule } }); } @@ -730,6 +792,7 @@ fn client_services( where T: atrium_xrpc::XrpcClient + Send + Sync, { #(#fields)* + pub(crate) _phantom: core::marker::PhantomData, } }; Ok(quote! { @@ -738,7 +801,11 @@ fn client_services( }) } -fn client_new(tree: &HashMap>, key: &str) -> Result { +fn client_new( + tree: &HashMap>, + key: &str, + namespaces: &[(&str, Option<&str>)], +) -> Result { let children = tree[key].iter().sorted().collect_vec(); let mut members = Vec::new(); for (name, is_leaf) in &children { @@ -750,9 +817,18 @@ fn client_new(tree: &HashMap>, key: &str) -> Resul } else { key.split('.').chain([*name]).collect_vec() }; + let namespace = parts.join("."); + let feature = if let Some((_, Some(feature_name))) = + namespaces.iter().find(|(prefix, _)| prefix == &namespace) + { + quote!(#[cfg(feature = #feature_name)]) + } else { + quote!() + }; let path = syn::parse_str::(&parts.join("::"))?; let name = format_ident!("{}", name.to_snake_case()); members.push(quote! { + #feature #name: #path::Service::new(std::sync::Arc::clone(&xrpc)), }); } @@ -762,9 +838,11 @@ fn client_new(tree: &HashMap>, key: &str) -> Resul }); } Ok(quote! { + #[allow(unused_variables)] pub(crate) fn new(xrpc: std::sync::Arc) -> Self { Self { #(#members)* + _phantom: core::marker::PhantomData, } } }) diff --git a/lexicon/lexgen/src/bin/main.rs b/lexicon/lexgen/src/bin/main.rs index f5eb8244..217aeae5 100644 --- a/lexicon/lexgen/src/bin/main.rs +++ b/lexicon/lexgen/src/bin/main.rs @@ -17,7 +17,12 @@ fn main() -> Result<(), Box> { let results = genapi( &args.lexdir, &args.outdir, - &["app.bsky", "chat.bsky", "com.atproto", "tools.ozone"], + &[ + ("com.atproto", None), + ("app.bsky", Some("namespace-appbsky")), + ("chat.bsky", Some("namespace-chatbsky")), + ("tools.ozone", Some("namespace-toolsozone")), + ], )?; for path in &results { println!(