From 6060a7cda9f810291f24b52ca5f80bbcdcc8b06b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 18 Feb 2024 00:54:22 +0000 Subject: [PATCH] Introduce dedicated types for DID and handle Lexicon string formats --- Cargo.lock | 1 + Cargo.toml | 3 +- atrium-api/CHANGELOG.md | 6 + atrium-api/Cargo.toml | 1 + atrium-api/README.md | 2 +- atrium-api/src/agent.rs | 14 +- atrium-api/src/app/bsky/actor/defs.rs | 12 +- atrium-api/src/app/bsky/actor/get_profile.rs | 2 +- atrium-api/src/app/bsky/actor/get_profiles.rs | 2 +- atrium-api/src/app/bsky/feed/defs.rs | 4 +- .../app/bsky/feed/describe_feed_generator.rs | 2 +- atrium-api/src/app/bsky/feed/generator.rs | 2 +- .../src/app/bsky/feed/get_actor_feeds.rs | 2 +- .../src/app/bsky/feed/get_actor_likes.rs | 2 +- .../src/app/bsky/feed/get_author_feed.rs | 2 +- atrium-api/src/app/bsky/graph/block.rs | 2 +- atrium-api/src/app/bsky/graph/defs.rs | 4 +- atrium-api/src/app/bsky/graph/follow.rs | 2 +- .../src/app/bsky/graph/get_followers.rs | 2 +- atrium-api/src/app/bsky/graph/get_follows.rs | 2 +- atrium-api/src/app/bsky/graph/get_lists.rs | 2 +- .../src/app/bsky/graph/get_relationships.rs | 6 +- .../graph/get_suggested_follows_by_actor.rs | 2 +- atrium-api/src/app/bsky/graph/listitem.rs | 2 +- atrium-api/src/app/bsky/graph/mute_actor.rs | 2 +- atrium-api/src/app/bsky/graph/unmute_actor.rs | 2 +- .../app/bsky/notification/register_push.rs | 2 +- atrium-api/src/app/bsky/richtext/facet.rs | 2 +- atrium-api/src/app/bsky/unspecced/defs.rs | 2 +- .../admin/create_communication_template.rs | 2 +- atrium-api/src/com/atproto/admin/defs.rs | 30 +- .../src/com/atproto/admin/delete_account.rs | 2 +- .../atproto/admin/disable_account_invites.rs | 2 +- .../atproto/admin/emit_moderation_event.rs | 2 +- .../atproto/admin/enable_account_invites.rs | 2 +- .../src/com/atproto/admin/get_account_info.rs | 2 +- .../com/atproto/admin/get_account_infos.rs | 2 +- atrium-api/src/com/atproto/admin/get_repo.rs | 2 +- .../com/atproto/admin/get_subject_status.rs | 2 +- .../atproto/admin/query_moderation_events.rs | 2 +- .../admin/query_moderation_statuses.rs | 2 +- .../src/com/atproto/admin/send_email.rs | 4 +- .../com/atproto/admin/update_account_email.rs | 2 +- .../atproto/admin/update_account_handle.rs | 4 +- .../admin/update_communication_template.rs | 2 +- .../com/atproto/identity/resolve_handle.rs | 4 +- .../src/com/atproto/identity/update_handle.rs | 2 +- atrium-api/src/com/atproto/label/defs.rs | 2 +- .../src/com/atproto/label/query_labels.rs | 2 +- .../com/atproto/moderation/create_report.rs | 2 +- .../src/com/atproto/repo/apply_writes.rs | 2 +- .../src/com/atproto/repo/create_record.rs | 2 +- .../src/com/atproto/repo/delete_record.rs | 2 +- .../src/com/atproto/repo/describe_repo.rs | 6 +- atrium-api/src/com/atproto/repo/get_record.rs | 2 +- .../src/com/atproto/repo/list_records.rs | 2 +- atrium-api/src/com/atproto/repo/put_record.rs | 2 +- .../src/com/atproto/server/create_account.rs | 8 +- .../com/atproto/server/create_invite_code.rs | 2 +- .../com/atproto/server/create_invite_codes.rs | 2 +- .../src/com/atproto/server/create_session.rs | 4 +- atrium-api/src/com/atproto/server/defs.rs | 2 +- .../src/com/atproto/server/delete_account.rs | 2 +- .../src/com/atproto/server/get_session.rs | 4 +- .../src/com/atproto/server/refresh_session.rs | 4 +- atrium-api/src/com/atproto/sync/get_blob.rs | 2 +- atrium-api/src/com/atproto/sync/get_blocks.rs | 2 +- .../src/com/atproto/sync/get_checkout.rs | 2 +- atrium-api/src/com/atproto/sync/get_head.rs | 2 +- .../src/com/atproto/sync/get_latest_commit.rs | 2 +- atrium-api/src/com/atproto/sync/get_record.rs | 2 +- atrium-api/src/com/atproto/sync/get_repo.rs | 2 +- atrium-api/src/com/atproto/sync/list_blobs.rs | 2 +- atrium-api/src/com/atproto/sync/list_repos.rs | 2 +- .../src/com/atproto/sync/subscribe_repos.rs | 10 +- .../src/com/atproto/temp/transfer_account.rs | 8 +- atrium-api/src/types.rs | 2 + atrium-api/src/types/string.rs | 285 ++++++++++++++++++ atrium-cli/src/commands.rs | 5 +- atrium-cli/src/runner.rs | 27 +- lexicon/atrium-codegen/src/token_stream.rs | 11 +- 81 files changed, 448 insertions(+), 131 deletions(-) create mode 100644 atrium-api/src/types/string.rs diff --git a/Cargo.lock b/Cargo.lock index c8acd1d0..57dc5985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,6 +404,7 @@ dependencies = [ "futures", "http", "libipld-core", + "regex", "serde", "serde_bytes", "serde_ipld_dagcbor 0.4.2", diff --git a/Cargo.toml b/Cargo.toml index 98afcd7c..29d8ab14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,8 @@ cid = "0.10.1" libipld-core = "0.16" serde_ipld_dagcbor = "0.3" -# Serde +# Parsing and validation +regex = "1" serde = "1.0.160" serde_bytes = "0.11.9" serde_json = "1.0.96" diff --git a/atrium-api/CHANGELOG.md b/atrium-api/CHANGELOG.md index fcc0ade7..0b0bbda9 100644 --- a/atrium-api/CHANGELOG.md +++ b/atrium-api/CHANGELOG.md @@ -12,10 +12,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `LimitedU16`, `LimitedNonZeroU16`, `BoundedU16` - `LimitedU32`, `LimitedNonZeroU32`, `BoundedU32` - `LimitedU64`, `LimitedNonZeroU64`, `BoundedU64` + - `string` module, containing dedicated types for formatted Lexicon strings. ### Changed - All Lexicon integer fields now have a type that matches their minimum and maximum accepted values, instead of `i32`. +- All Lexicon string fields with one of the following formats now have the corresponding + dedicated type, instead of `String`: + - `at-identifier` (`atrium_api::types::string::AtIdentifier`) + - `did` (`atrium_api::types::string::Did`) + - `handle` (`atrium_api::types::string::Handle`) ## [0.16.0](https://github.com/sugyan/atrium/compare/atrium-api-v0.15.0...atrium-api-v0.16.0) - 2024-02-09 diff --git a/atrium-api/Cargo.toml b/atrium-api/Cargo.toml index 9e391e63..348f6752 100644 --- a/atrium-api/Cargo.toml +++ b/atrium-api/Cargo.toml @@ -15,6 +15,7 @@ keywords.workspace = true atrium-xrpc.workspace = true async-trait.workspace = true http.workspace = true +regex.workspace = true serde = { workspace = true, features = ["derive"] } serde_bytes.workspace = true tokio = { workspace = true, optional = true } diff --git a/atrium-api/README.md b/atrium-api/README.md index e7dab192..c3ef835a 100644 --- a/atrium-api/README.md +++ b/atrium-api/README.md @@ -56,7 +56,7 @@ async fn main() -> Result<(), Box> { .bsky .actor .get_profile(atrium_api::app::bsky::actor::get_profile::Parameters { - actor: "bsky.app".into(), + actor: "bsky.app".parse()?, }) .await?; println!("{:?}", result); diff --git a/atrium-api/src/agent.rs b/atrium-api/src/agent.rs index 61182787..f8cad61f 100644 --- a/atrium-api/src/agent.rs +++ b/atrium-api/src/agent.rs @@ -152,9 +152,9 @@ mod tests { body.extend(serde_json::to_vec( &crate::com::atproto::server::refresh_session::Output { access_jwt: String::from("access"), - did: String::from("did"), + did: "did:web:example.com".parse().expect("valid"), did_doc: None, - handle: String::from("handle"), + handle: "example.com".parse().expect("valid"), refresh_jwt: String::from("refresh"), }, )?); @@ -187,11 +187,11 @@ mod tests { fn session() -> Session { Session { access_jwt: String::from("access"), - did: String::from("did"), + did: "did:web:example.com".parse().expect("valid"), did_doc: None, email: None, email_confirmed: None, - handle: String::from("handle"), + handle: "example.com".parse().expect("valid"), refresh_jwt: String::from("refresh"), } } @@ -266,7 +266,7 @@ mod tests { .get_session() .await .expect("get session should be succeeded"); - assert_eq!(output.did, "did"); + assert_eq!(output.did.as_str(), "did:web:example.com"); } #[tokio::test] @@ -296,7 +296,7 @@ mod tests { .get_session() .await .expect("get session should be succeeded"); - assert_eq!(output.did, "did"); + assert_eq!(output.did.as_str(), "did:web:example.com"); assert_eq!( agent .store @@ -338,7 +338,7 @@ mod tests { .expect("task should be successfully executed") .as_ref() .expect("get session should be succeeded"); - assert_eq!(output.did, "did"); + assert_eq!(output.did.as_str(), "did:web:example.com"); } assert_eq!( agent diff --git a/atrium-api/src/app/bsky/actor/defs.rs b/atrium-api/src/app/bsky/actor/defs.rs index 4162b0d7..b63603d3 100644 --- a/atrium-api/src/app/bsky/actor/defs.rs +++ b/atrium-api/src/app/bsky/actor/defs.rs @@ -54,10 +54,10 @@ pub struct ProfileView { pub avatar: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, - pub handle: String, + pub handle: crate::types::string::Handle, #[serde(skip_serializing_if = "Option::is_none")] pub indexed_at: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -70,10 +70,10 @@ pub struct ProfileView { pub struct ProfileViewBasic { #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, - pub handle: String, + pub handle: crate::types::string::Handle, #[serde(skip_serializing_if = "Option::is_none")] pub labels: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -88,14 +88,14 @@ pub struct ProfileViewDetailed { pub banner: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub followers_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub follows_count: Option, - pub handle: String, + pub handle: crate::types::string::Handle, #[serde(skip_serializing_if = "Option::is_none")] pub indexed_at: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/actor/get_profile.rs b/atrium-api/src/app/bsky/actor/get_profile.rs index c85ed154..f33d0333 100644 --- a/atrium-api/src/app/bsky/actor/get_profile.rs +++ b/atrium-api/src/app/bsky/actor/get_profile.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, } pub type Output = crate::app::bsky::actor::defs::ProfileViewDetailed; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/app/bsky/actor/get_profiles.rs b/atrium-api/src/app/bsky/actor/get_profiles.rs index 835ed3c2..678e00bf 100644 --- a/atrium-api/src/app/bsky/actor/get_profiles.rs +++ b/atrium-api/src/app/bsky/actor/get_profiles.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actors: Vec, + pub actors: Vec, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/app/bsky/feed/defs.rs b/atrium-api/src/app/bsky/feed/defs.rs index 8f0efaf8..f40e818d 100644 --- a/atrium-api/src/app/bsky/feed/defs.rs +++ b/atrium-api/src/app/bsky/feed/defs.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct BlockedAuthor { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub viewer: Option, } @@ -34,7 +34,7 @@ pub struct GeneratorView { pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description_facets: Option>, - pub did: String, + pub did: crate::types::string::Did, pub display_name: String, pub indexed_at: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/feed/describe_feed_generator.rs b/atrium-api/src/app/bsky/feed/describe_feed_generator.rs index f46ac62a..5c82f075 100644 --- a/atrium-api/src/app/bsky/feed/describe_feed_generator.rs +++ b/atrium-api/src/app/bsky/feed/describe_feed_generator.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { - pub did: String, + pub did: crate::types::string::Did, pub feeds: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub links: Option, diff --git a/atrium-api/src/app/bsky/feed/generator.rs b/atrium-api/src/app/bsky/feed/generator.rs index 38b0c3a5..dea6d1fc 100644 --- a/atrium-api/src/app/bsky/feed/generator.rs +++ b/atrium-api/src/app/bsky/feed/generator.rs @@ -10,7 +10,7 @@ pub struct Record { pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description_facets: Option>, - pub did: String, + pub did: crate::types::string::Did, pub display_name: String, #[serde(skip_serializing_if = "Option::is_none")] pub labels: Option, diff --git a/atrium-api/src/app/bsky/feed/get_actor_feeds.rs b/atrium-api/src/app/bsky/feed/get_actor_feeds.rs index b52de815..58e89eaa 100644 --- a/atrium-api/src/app/bsky/feed/get_actor_feeds.rs +++ b/atrium-api/src/app/bsky/feed/get_actor_feeds.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/feed/get_actor_likes.rs b/atrium-api/src/app/bsky/feed/get_actor_likes.rs index acb015f6..7789ea56 100644 --- a/atrium-api/src/app/bsky/feed/get_actor_likes.rs +++ b/atrium-api/src/app/bsky/feed/get_actor_likes.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/feed/get_author_feed.rs b/atrium-api/src/app/bsky/feed/get_author_feed.rs index e91d011f..1bd2c3c3 100644 --- a/atrium-api/src/app/bsky/feed/get_author_feed.rs +++ b/atrium-api/src/app/bsky/feed/get_author_feed.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/graph/block.rs b/atrium-api/src/app/bsky/graph/block.rs index 609df1df..d2a849dd 100644 --- a/atrium-api/src/app/bsky/graph/block.rs +++ b/atrium-api/src/app/bsky/graph/block.rs @@ -4,5 +4,5 @@ #[serde(rename_all = "camelCase")] pub struct Record { pub created_at: String, - pub subject: String, + pub subject: crate::types::string::Did, } diff --git a/atrium-api/src/app/bsky/graph/defs.rs b/atrium-api/src/app/bsky/graph/defs.rs index 4baf32e0..f24c908e 100644 --- a/atrium-api/src/app/bsky/graph/defs.rs +++ b/atrium-api/src/app/bsky/graph/defs.rs @@ -55,14 +55,14 @@ pub struct Modlist; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct NotFoundActor { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, pub not_found: bool, } ///lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object) #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Relationship { - pub did: String, + pub did: crate::types::string::Did, ///if the actor is followed by this DID, contains the AT-URI of the follow record #[serde(skip_serializing_if = "Option::is_none")] pub followed_by: Option, diff --git a/atrium-api/src/app/bsky/graph/follow.rs b/atrium-api/src/app/bsky/graph/follow.rs index 5b7e5d4f..00c1e2bc 100644 --- a/atrium-api/src/app/bsky/graph/follow.rs +++ b/atrium-api/src/app/bsky/graph/follow.rs @@ -4,5 +4,5 @@ #[serde(rename_all = "camelCase")] pub struct Record { pub created_at: String, - pub subject: String, + pub subject: crate::types::string::Did, } diff --git a/atrium-api/src/app/bsky/graph/get_followers.rs b/atrium-api/src/app/bsky/graph/get_followers.rs index d46fed23..2e8acb9c 100644 --- a/atrium-api/src/app/bsky/graph/get_followers.rs +++ b/atrium-api/src/app/bsky/graph/get_followers.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/graph/get_follows.rs b/atrium-api/src/app/bsky/graph/get_follows.rs index c2864897..43280c9c 100644 --- a/atrium-api/src/app/bsky/graph/get_follows.rs +++ b/atrium-api/src/app/bsky/graph/get_follows.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/graph/get_lists.rs b/atrium-api/src/app/bsky/graph/get_lists.rs index 71e8e4a0..97415627 100644 --- a/atrium-api/src/app/bsky/graph/get_lists.rs +++ b/atrium-api/src/app/bsky/graph/get_lists.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/app/bsky/graph/get_relationships.rs b/atrium-api/src/app/bsky/graph/get_relationships.rs index 49b0f1ab..7e1e0ad6 100644 --- a/atrium-api/src/app/bsky/graph/get_relationships.rs +++ b/atrium-api/src/app/bsky/graph/get_relationships.rs @@ -3,15 +3,15 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] - pub others: Option>, + pub others: Option>, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { #[serde(skip_serializing_if = "Option::is_none")] - pub actor: Option, + pub actor: Option, pub relationships: Vec, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/app/bsky/graph/get_suggested_follows_by_actor.rs b/atrium-api/src/app/bsky/graph/get_suggested_follows_by_actor.rs index 40dd86b9..f33d5674 100644 --- a/atrium-api/src/app/bsky/graph/get_suggested_follows_by_actor.rs +++ b/atrium-api/src/app/bsky/graph/get_suggested_follows_by_actor.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/app/bsky/graph/listitem.rs b/atrium-api/src/app/bsky/graph/listitem.rs index ecd005e8..74efb4bd 100644 --- a/atrium-api/src/app/bsky/graph/listitem.rs +++ b/atrium-api/src/app/bsky/graph/listitem.rs @@ -5,5 +5,5 @@ pub struct Record { pub created_at: String, pub list: String, - pub subject: String, + pub subject: crate::types::string::Did, } diff --git a/atrium-api/src/app/bsky/graph/mute_actor.rs b/atrium-api/src/app/bsky/graph/mute_actor.rs index 3b369027..c3289f56 100644 --- a/atrium-api/src/app/bsky/graph/mute_actor.rs +++ b/atrium-api/src/app/bsky/graph/mute_actor.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/app/bsky/graph/unmute_actor.rs b/atrium-api/src/app/bsky/graph/unmute_actor.rs index ad1e7f98..91fcc9d7 100644 --- a/atrium-api/src/app/bsky/graph/unmute_actor.rs +++ b/atrium-api/src/app/bsky/graph/unmute_actor.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub actor: String, + pub actor: crate::types::string::AtIdentifier, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/app/bsky/notification/register_push.rs b/atrium-api/src/app/bsky/notification/register_push.rs index 467bab98..a5891f67 100644 --- a/atrium-api/src/app/bsky/notification/register_push.rs +++ b/atrium-api/src/app/bsky/notification/register_push.rs @@ -5,7 +5,7 @@ pub struct Input { pub app_id: String, pub platform: String, - pub service_did: String, + pub service_did: crate::types::string::Did, pub token: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/app/bsky/richtext/facet.rs b/atrium-api/src/app/bsky/richtext/facet.rs index b0e54935..c6c1509a 100644 --- a/atrium-api/src/app/bsky/richtext/facet.rs +++ b/atrium-api/src/app/bsky/richtext/facet.rs @@ -23,7 +23,7 @@ pub struct Link { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Mention { - pub did: String, + pub did: crate::types::string::Did, } ///A hashtag. #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/app/bsky/unspecced/defs.rs b/atrium-api/src/app/bsky/unspecced/defs.rs index 75005be2..5b49e23a 100644 --- a/atrium-api/src/app/bsky/unspecced/defs.rs +++ b/atrium-api/src/app/bsky/unspecced/defs.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct SkeletonSearchActor { - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/com/atproto/admin/create_communication_template.rs b/atrium-api/src/com/atproto/admin/create_communication_template.rs index 25433405..d464dbad 100644 --- a/atrium-api/src/com/atproto/admin/create_communication_template.rs +++ b/atrium-api/src/com/atproto/admin/create_communication_template.rs @@ -7,7 +7,7 @@ pub struct Input { pub content_markdown: String, ///DID of the user who is creating the template. #[serde(skip_serializing_if = "Option::is_none")] - pub created_by: Option, + pub created_by: Option, ///Name of the template. pub name: String, ///Subject of the message, used in emails. diff --git a/atrium-api/src/com/atproto/admin/defs.rs b/atrium-api/src/com/atproto/admin/defs.rs index 2d653946..418ff476 100644 --- a/atrium-api/src/com/atproto/admin/defs.rs +++ b/atrium-api/src/com/atproto/admin/defs.rs @@ -3,12 +3,12 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AccountView { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email_confirmed_at: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub indexed_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub invite_note: Option, @@ -42,7 +42,7 @@ pub struct CommunicationTemplateView { pub disabled: bool, pub id: String, ///DID of the user who last updated the template. - pub last_updated_by: String, + pub last_updated_by: crate::types::string::Did, ///Name of the template. pub name: String, ///Content of the template, can contain markdown and variable placeholders. @@ -151,7 +151,7 @@ pub struct ModEventUnmute { #[serde(rename_all = "camelCase")] pub struct ModEventView { pub created_at: String, - pub created_by: String, + pub created_by: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub creator_handle: Option, pub event: ModEventViewEventEnum, @@ -165,7 +165,7 @@ pub struct ModEventView { #[serde(rename_all = "camelCase")] pub struct ModEventViewDetail { pub created_at: String, - pub created_by: String, + pub created_by: crate::types::string::Did, pub event: ModEventViewDetailEventEnum, pub id: i64, pub subject: ModEventViewDetailSubjectEnum, @@ -216,22 +216,22 @@ pub struct RecordViewNotFound { #[serde(rename_all = "camelCase")] pub struct RepoBlobRef { pub cid: String, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub record_uri: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RepoRef { - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RepoView { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub indexed_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub invite_note: Option, @@ -245,12 +245,12 @@ pub struct RepoView { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RepoViewDetail { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email_confirmed_at: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub indexed_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub invite_note: Option, @@ -268,7 +268,7 @@ pub struct RepoViewDetail { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RepoViewNotFound { - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -278,7 +278,7 @@ pub struct ReportView { pub created_at: String, pub id: i64, pub reason_type: crate::com::atproto::moderation::defs::ReasonType, - pub reported_by: String, + pub reported_by: crate::types::string::Did, pub resolved_by_action_ids: Vec, pub subject: ReportViewSubjectEnum, #[serde(skip_serializing_if = "Option::is_none")] @@ -292,7 +292,7 @@ pub struct ReportViewDetail { pub created_at: String, pub id: i64, pub reason_type: crate::com::atproto::moderation::defs::ReasonType, - pub reported_by: String, + pub reported_by: crate::types::string::Did, pub resolved_by_actions: Vec, pub subject: ReportViewDetailSubjectEnum, #[serde(skip_serializing_if = "Option::is_none")] @@ -332,7 +332,7 @@ pub struct SubjectStatusView { #[serde(skip_serializing_if = "Option::is_none")] pub last_reviewed_at: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub last_reviewed_by: Option, + pub last_reviewed_by: Option, #[serde(skip_serializing_if = "Option::is_none")] pub mute_until: Option, pub review_state: SubjectReviewState, diff --git a/atrium-api/src/com/atproto/admin/delete_account.rs b/atrium-api/src/com/atproto/admin/delete_account.rs index c050e6f9..05621eff 100644 --- a/atrium-api/src/com/atproto/admin/delete_account.rs +++ b/atrium-api/src/com/atproto/admin/delete_account.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/admin/disable_account_invites.rs b/atrium-api/src/com/atproto/admin/disable_account_invites.rs index 3f5e7428..9c706bff 100644 --- a/atrium-api/src/com/atproto/admin/disable_account_invites.rs +++ b/atrium-api/src/com/atproto/admin/disable_account_invites.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub account: String, + pub account: crate::types::string::Did, ///Optional reason for disabled invites. #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, diff --git a/atrium-api/src/com/atproto/admin/emit_moderation_event.rs b/atrium-api/src/com/atproto/admin/emit_moderation_event.rs index 2bc0c209..2532bbcd 100644 --- a/atrium-api/src/com/atproto/admin/emit_moderation_event.rs +++ b/atrium-api/src/com/atproto/admin/emit_moderation_event.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub created_by: String, + pub created_by: crate::types::string::Did, pub event: InputEventEnum, pub subject: InputSubjectEnum, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/atrium-api/src/com/atproto/admin/enable_account_invites.rs b/atrium-api/src/com/atproto/admin/enable_account_invites.rs index 51ceae2c..543d409d 100644 --- a/atrium-api/src/com/atproto/admin/enable_account_invites.rs +++ b/atrium-api/src/com/atproto/admin/enable_account_invites.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub account: String, + pub account: crate::types::string::Did, ///Optional reason for enabled invites. #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, diff --git a/atrium-api/src/com/atproto/admin/get_account_info.rs b/atrium-api/src/com/atproto/admin/get_account_info.rs index 2f93c559..ad0dad85 100644 --- a/atrium-api/src/com/atproto/admin/get_account_info.rs +++ b/atrium-api/src/com/atproto/admin/get_account_info.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub did: String, + pub did: crate::types::string::Did, } pub type Output = crate::com::atproto::admin::defs::AccountView; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/admin/get_account_infos.rs b/atrium-api/src/com/atproto/admin/get_account_infos.rs index a33e3898..f5da7e3c 100644 --- a/atrium-api/src/com/atproto/admin/get_account_infos.rs +++ b/atrium-api/src/com/atproto/admin/get_account_infos.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub dids: Vec, + pub dids: Vec, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/com/atproto/admin/get_repo.rs b/atrium-api/src/com/atproto/admin/get_repo.rs index 9f3011f4..2f640c06 100644 --- a/atrium-api/src/com/atproto/admin/get_repo.rs +++ b/atrium-api/src/com/atproto/admin/get_repo.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Parameters { - pub did: String, + pub did: crate::types::string::Did, } pub type Output = crate::com::atproto::admin::defs::RepoViewDetail; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/admin/get_subject_status.rs b/atrium-api/src/com/atproto/admin/get_subject_status.rs index 2f65ae0f..4a06d3a8 100644 --- a/atrium-api/src/com/atproto/admin/get_subject_status.rs +++ b/atrium-api/src/com/atproto/admin/get_subject_status.rs @@ -6,7 +6,7 @@ pub struct Parameters { #[serde(skip_serializing_if = "Option::is_none")] pub blob: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub did: Option, + pub did: Option, #[serde(skip_serializing_if = "Option::is_none")] pub uri: Option, } diff --git a/atrium-api/src/com/atproto/admin/query_moderation_events.rs b/atrium-api/src/com/atproto/admin/query_moderation_events.rs index c26208d0..1ad41de6 100644 --- a/atrium-api/src/com/atproto/admin/query_moderation_events.rs +++ b/atrium-api/src/com/atproto/admin/query_moderation_events.rs @@ -16,7 +16,7 @@ pub struct Parameters { #[serde(skip_serializing_if = "Option::is_none")] pub created_before: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub created_by: Option, + pub created_by: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, ///If true, only events with comments are returned diff --git a/atrium-api/src/com/atproto/admin/query_moderation_statuses.rs b/atrium-api/src/com/atproto/admin/query_moderation_statuses.rs index 8bd22b46..f5e2714a 100644 --- a/atrium-api/src/com/atproto/admin/query_moderation_statuses.rs +++ b/atrium-api/src/com/atproto/admin/query_moderation_statuses.rs @@ -18,7 +18,7 @@ pub struct Parameters { pub include_muted: Option, ///Get all subject statuses that were reviewed by a specific moderator #[serde(skip_serializing_if = "Option::is_none")] - pub last_reviewed_by: Option, + pub last_reviewed_by: Option, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option>, ///Search subjects reported after a given timestamp diff --git a/atrium-api/src/com/atproto/admin/send_email.rs b/atrium-api/src/com/atproto/admin/send_email.rs index 4650c2ab..b0321790 100644 --- a/atrium-api/src/com/atproto/admin/send_email.rs +++ b/atrium-api/src/com/atproto/admin/send_email.rs @@ -7,8 +7,8 @@ pub struct Input { #[serde(skip_serializing_if = "Option::is_none")] pub comment: Option, pub content: String, - pub recipient_did: String, - pub sender_did: String, + pub recipient_did: crate::types::string::Did, + pub sender_did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub subject: Option, } diff --git a/atrium-api/src/com/atproto/admin/update_account_email.rs b/atrium-api/src/com/atproto/admin/update_account_email.rs index bd7ad3c4..b8fcb5cb 100644 --- a/atrium-api/src/com/atproto/admin/update_account_email.rs +++ b/atrium-api/src/com/atproto/admin/update_account_email.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Input { ///The handle or DID of the repo. - pub account: String, + pub account: crate::types::string::AtIdentifier, pub email: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/admin/update_account_handle.rs b/atrium-api/src/com/atproto/admin/update_account_handle.rs index e29416a4..63e7b93d 100644 --- a/atrium-api/src/com/atproto/admin/update_account_handle.rs +++ b/atrium-api/src/com/atproto/admin/update_account_handle.rs @@ -3,8 +3,8 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub did: String, - pub handle: String, + pub did: crate::types::string::Did, + pub handle: crate::types::string::Handle, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/admin/update_communication_template.rs b/atrium-api/src/com/atproto/admin/update_communication_template.rs index d2401424..14c04dc0 100644 --- a/atrium-api/src/com/atproto/admin/update_communication_template.rs +++ b/atrium-api/src/com/atproto/admin/update_communication_template.rs @@ -18,7 +18,7 @@ pub struct Input { pub subject: Option, ///DID of the user who is updating the template. #[serde(skip_serializing_if = "Option::is_none")] - pub updated_by: Option, + pub updated_by: Option, } pub type Output = crate::com::atproto::admin::defs::CommunicationTemplateView; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/identity/resolve_handle.rs b/atrium-api/src/com/atproto/identity/resolve_handle.rs index 82f308a6..617e96cc 100644 --- a/atrium-api/src/com/atproto/identity/resolve_handle.rs +++ b/atrium-api/src/com/atproto/identity/resolve_handle.rs @@ -4,12 +4,12 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The handle to resolve. - pub handle: String, + pub handle: crate::types::string::Handle, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/identity/update_handle.rs b/atrium-api/src/com/atproto/identity/update_handle.rs index b5f2478c..b848edbb 100644 --- a/atrium-api/src/com/atproto/identity/update_handle.rs +++ b/atrium-api/src/com/atproto/identity/update_handle.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub handle: String, + pub handle: crate::types::string::Handle, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/label/defs.rs b/atrium-api/src/com/atproto/label/defs.rs index 17e10335..a6756ed4 100644 --- a/atrium-api/src/com/atproto/label/defs.rs +++ b/atrium-api/src/com/atproto/label/defs.rs @@ -13,7 +13,7 @@ pub struct Label { #[serde(skip_serializing_if = "Option::is_none")] pub neg: Option, ///DID of the actor who created this label. - pub src: String, + pub src: crate::types::string::Did, ///AT URI of the record, repository (account), or other resource that this label applies to. pub uri: String, ///The short string name of the value or type of this label. diff --git a/atrium-api/src/com/atproto/label/query_labels.rs b/atrium-api/src/com/atproto/label/query_labels.rs index 16382829..017bc183 100644 --- a/atrium-api/src/com/atproto/label/query_labels.rs +++ b/atrium-api/src/com/atproto/label/query_labels.rs @@ -9,7 +9,7 @@ pub struct Parameters { pub limit: Option>, ///Optional list of label sources (DIDs) to filter on. #[serde(skip_serializing_if = "Option::is_none")] - pub sources: Option>, + pub sources: Option>, ///List of AT URI patterns to match (boolean 'OR'). Each may be a prefix (ending with '*'; will match inclusive of the string leading to '*'), or a full URI. pub uri_patterns: Vec, } diff --git a/atrium-api/src/com/atproto/moderation/create_report.rs b/atrium-api/src/com/atproto/moderation/create_report.rs index b1886c9d..502f01e2 100644 --- a/atrium-api/src/com/atproto/moderation/create_report.rs +++ b/atrium-api/src/com/atproto/moderation/create_report.rs @@ -16,7 +16,7 @@ pub struct Output { #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, pub reason_type: crate::com::atproto::moderation::defs::ReasonType, - pub reported_by: String, + pub reported_by: crate::types::string::Did, pub subject: OutputSubjectEnum, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/repo/apply_writes.rs b/atrium-api/src/com/atproto/repo/apply_writes.rs index bdc736de..1b23e260 100644 --- a/atrium-api/src/com/atproto/repo/apply_writes.rs +++ b/atrium-api/src/com/atproto/repo/apply_writes.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Input { ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, #[serde(skip_serializing_if = "Option::is_none")] pub swap_commit: Option, ///Flag for validating the records. diff --git a/atrium-api/src/com/atproto/repo/create_record.rs b/atrium-api/src/com/atproto/repo/create_record.rs index f1b9698c..d222d191 100644 --- a/atrium-api/src/com/atproto/repo/create_record.rs +++ b/atrium-api/src/com/atproto/repo/create_record.rs @@ -8,7 +8,7 @@ pub struct Input { ///The record to create. pub record: crate::records::Record, ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, ///The key of the record. #[serde(skip_serializing_if = "Option::is_none")] pub rkey: Option, diff --git a/atrium-api/src/com/atproto/repo/delete_record.rs b/atrium-api/src/com/atproto/repo/delete_record.rs index 5816b9a5..97b2f6dd 100644 --- a/atrium-api/src/com/atproto/repo/delete_record.rs +++ b/atrium-api/src/com/atproto/repo/delete_record.rs @@ -6,7 +6,7 @@ pub struct Input { ///The NSID of the record collection. pub collection: String, ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, ///The key of the record. pub rkey: String, ///Compare and swap with the previous commit by CID. diff --git a/atrium-api/src/com/atproto/repo/describe_repo.rs b/atrium-api/src/com/atproto/repo/describe_repo.rs index 636207b1..dac5db70 100644 --- a/atrium-api/src/com/atproto/repo/describe_repo.rs +++ b/atrium-api/src/com/atproto/repo/describe_repo.rs @@ -4,15 +4,15 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { pub collections: Vec, - pub did: String, + pub did: crate::types::string::Did, pub did_doc: crate::did_doc::DidDocument, - pub handle: String, + pub handle: crate::types::string::Handle, pub handle_is_correct: bool, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/repo/get_record.rs b/atrium-api/src/com/atproto/repo/get_record.rs index fe6492ff..c8bc2501 100644 --- a/atrium-api/src/com/atproto/repo/get_record.rs +++ b/atrium-api/src/com/atproto/repo/get_record.rs @@ -9,7 +9,7 @@ pub struct Parameters { ///The NSID of the record collection. pub collection: String, ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, ///The key of the record. pub rkey: String, } diff --git a/atrium-api/src/com/atproto/repo/list_records.rs b/atrium-api/src/com/atproto/repo/list_records.rs index 08394c5a..37f18af8 100644 --- a/atrium-api/src/com/atproto/repo/list_records.rs +++ b/atrium-api/src/com/atproto/repo/list_records.rs @@ -11,7 +11,7 @@ pub struct Parameters { #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option>, ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, ///Flag to reverse the order of the returned records. #[serde(skip_serializing_if = "Option::is_none")] pub reverse: Option, diff --git a/atrium-api/src/com/atproto/repo/put_record.rs b/atrium-api/src/com/atproto/repo/put_record.rs index ca186beb..b21f5647 100644 --- a/atrium-api/src/com/atproto/repo/put_record.rs +++ b/atrium-api/src/com/atproto/repo/put_record.rs @@ -8,7 +8,7 @@ pub struct Input { ///The record to write. pub record: crate::records::Record, ///The handle or DID of the repo. - pub repo: String, + pub repo: crate::types::string::AtIdentifier, ///The key of the record. pub rkey: String, ///Compare and swap with the previous commit by CID. diff --git a/atrium-api/src/com/atproto/server/create_account.rs b/atrium-api/src/com/atproto/server/create_account.rs index d65133e8..2b761d94 100644 --- a/atrium-api/src/com/atproto/server/create_account.rs +++ b/atrium-api/src/com/atproto/server/create_account.rs @@ -4,10 +4,10 @@ #[serde(rename_all = "camelCase")] pub struct Input { #[serde(skip_serializing_if = "Option::is_none")] - pub did: Option, + pub did: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, - pub handle: String, + pub handle: crate::types::string::Handle, #[serde(skip_serializing_if = "Option::is_none")] pub invite_code: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -25,10 +25,10 @@ pub struct Input { #[serde(rename_all = "camelCase")] pub struct Output { pub access_jwt: String, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub did_doc: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub refresh_jwt: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/server/create_invite_code.rs b/atrium-api/src/com/atproto/server/create_invite_code.rs index f499065c..e58fa62e 100644 --- a/atrium-api/src/com/atproto/server/create_invite_code.rs +++ b/atrium-api/src/com/atproto/server/create_invite_code.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Input { #[serde(skip_serializing_if = "Option::is_none")] - pub for_account: Option, + pub for_account: Option, pub use_count: i64, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/server/create_invite_codes.rs b/atrium-api/src/com/atproto/server/create_invite_codes.rs index 76f74dde..a2da4f37 100644 --- a/atrium-api/src/com/atproto/server/create_invite_codes.rs +++ b/atrium-api/src/com/atproto/server/create_invite_codes.rs @@ -5,7 +5,7 @@ pub struct Input { pub code_count: i64, #[serde(skip_serializing_if = "Option::is_none")] - pub for_accounts: Option>, + pub for_accounts: Option>, pub use_count: i64, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/server/create_session.rs b/atrium-api/src/com/atproto/server/create_session.rs index 0d640b74..589d07ce 100644 --- a/atrium-api/src/com/atproto/server/create_session.rs +++ b/atrium-api/src/com/atproto/server/create_session.rs @@ -11,14 +11,14 @@ pub struct Input { #[serde(rename_all = "camelCase")] pub struct Output { pub access_jwt: String, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub did_doc: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email_confirmed: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub refresh_jwt: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/server/defs.rs b/atrium-api/src/com/atproto/server/defs.rs index 1c45e02a..0af1397d 100644 --- a/atrium-api/src/com/atproto/server/defs.rs +++ b/atrium-api/src/com/atproto/server/defs.rs @@ -15,5 +15,5 @@ pub struct InviteCode { #[serde(rename_all = "camelCase")] pub struct InviteCodeUse { pub used_at: String, - pub used_by: String, + pub used_by: crate::types::string::Did, } diff --git a/atrium-api/src/com/atproto/server/delete_account.rs b/atrium-api/src/com/atproto/server/delete_account.rs index fc28eada..d9667c26 100644 --- a/atrium-api/src/com/atproto/server/delete_account.rs +++ b/atrium-api/src/com/atproto/server/delete_account.rs @@ -3,7 +3,7 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub did: String, + pub did: crate::types::string::Did, pub password: String, pub token: String, } diff --git a/atrium-api/src/com/atproto/server/get_session.rs b/atrium-api/src/com/atproto/server/get_session.rs index a3409f75..71a5b24b 100644 --- a/atrium-api/src/com/atproto/server/get_session.rs +++ b/atrium-api/src/com/atproto/server/get_session.rs @@ -3,14 +3,14 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub did_doc: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(skip_serializing_if = "Option::is_none")] pub email_confirmed: Option, - pub handle: String, + pub handle: crate::types::string::Handle, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/server/refresh_session.rs b/atrium-api/src/com/atproto/server/refresh_session.rs index 9d420380..b120b235 100644 --- a/atrium-api/src/com/atproto/server/refresh_session.rs +++ b/atrium-api/src/com/atproto/server/refresh_session.rs @@ -4,10 +4,10 @@ #[serde(rename_all = "camelCase")] pub struct Output { pub access_jwt: String, - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub did_doc: Option, - pub handle: String, + pub handle: crate::types::string::Handle, pub refresh_jwt: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/sync/get_blob.rs b/atrium-api/src/com/atproto/sync/get_blob.rs index 86045f3e..4dcd3f2c 100644 --- a/atrium-api/src/com/atproto/sync/get_blob.rs +++ b/atrium-api/src/com/atproto/sync/get_blob.rs @@ -6,7 +6,7 @@ pub struct Parameters { ///The CID of the blob to fetch pub cid: String, ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/sync/get_blocks.rs b/atrium-api/src/com/atproto/sync/get_blocks.rs index 4fcbbaf6..fa4413f9 100644 --- a/atrium-api/src/com/atproto/sync/get_blocks.rs +++ b/atrium-api/src/com/atproto/sync/get_blocks.rs @@ -5,7 +5,7 @@ pub struct Parameters { pub cids: Vec, ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/sync/get_checkout.rs b/atrium-api/src/com/atproto/sync/get_checkout.rs index 6b2b28ba..9626ef5c 100644 --- a/atrium-api/src/com/atproto/sync/get_checkout.rs +++ b/atrium-api/src/com/atproto/sync/get_checkout.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] diff --git a/atrium-api/src/com/atproto/sync/get_head.rs b/atrium-api/src/com/atproto/sync/get_head.rs index bda84d85..7c586dc0 100644 --- a/atrium-api/src/com/atproto/sync/get_head.rs +++ b/atrium-api/src/com/atproto/sync/get_head.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/com/atproto/sync/get_latest_commit.rs b/atrium-api/src/com/atproto/sync/get_latest_commit.rs index f7a569d7..8e0234ad 100644 --- a/atrium-api/src/com/atproto/sync/get_latest_commit.rs +++ b/atrium-api/src/com/atproto/sync/get_latest_commit.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/atrium-api/src/com/atproto/sync/get_record.rs b/atrium-api/src/com/atproto/sync/get_record.rs index 8b709b97..5537951b 100644 --- a/atrium-api/src/com/atproto/sync/get_record.rs +++ b/atrium-api/src/com/atproto/sync/get_record.rs @@ -8,7 +8,7 @@ pub struct Parameters { #[serde(skip_serializing_if = "Option::is_none")] pub commit: Option, ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, pub rkey: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/com/atproto/sync/get_repo.rs b/atrium-api/src/com/atproto/sync/get_repo.rs index c7ce7280..d3b8c393 100644 --- a/atrium-api/src/com/atproto/sync/get_repo.rs +++ b/atrium-api/src/com/atproto/sync/get_repo.rs @@ -4,7 +4,7 @@ #[serde(rename_all = "camelCase")] pub struct Parameters { ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, ///The revision of the repo to catch up from. #[serde(skip_serializing_if = "Option::is_none")] pub since: Option, diff --git a/atrium-api/src/com/atproto/sync/list_blobs.rs b/atrium-api/src/com/atproto/sync/list_blobs.rs index 2c837bfd..dd981fb1 100644 --- a/atrium-api/src/com/atproto/sync/list_blobs.rs +++ b/atrium-api/src/com/atproto/sync/list_blobs.rs @@ -6,7 +6,7 @@ pub struct Parameters { #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, ///The DID of the repo. - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option>, ///Optional revision of the repo to list blobs since. diff --git a/atrium-api/src/com/atproto/sync/list_repos.rs b/atrium-api/src/com/atproto/sync/list_repos.rs index 3847403b..a650230f 100644 --- a/atrium-api/src/com/atproto/sync/list_repos.rs +++ b/atrium-api/src/com/atproto/sync/list_repos.rs @@ -21,7 +21,7 @@ pub enum Error {} #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Repo { - pub did: String, + pub did: crate::types::string::Did, pub head: String, pub rev: String, } diff --git a/atrium-api/src/com/atproto/sync/subscribe_repos.rs b/atrium-api/src/com/atproto/sync/subscribe_repos.rs index a123efc3..a59bbce0 100644 --- a/atrium-api/src/com/atproto/sync/subscribe_repos.rs +++ b/atrium-api/src/com/atproto/sync/subscribe_repos.rs @@ -26,7 +26,7 @@ pub struct Commit { #[serde(skip_serializing_if = "Option::is_none")] pub prev: Option, pub rebase: bool, - pub repo: String, + pub repo: crate::types::string::Did, ///The rev of the emitted commit. pub rev: String, pub seq: i64, @@ -39,8 +39,8 @@ pub struct Commit { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Handle { - pub did: String, - pub handle: String, + pub did: crate::types::string::Did, + pub handle: crate::types::string::Handle, pub seq: i64, pub time: String, } @@ -54,7 +54,7 @@ pub struct Info { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Migrate { - pub did: String, + pub did: crate::types::string::Did, #[serde(skip_serializing_if = "Option::is_none")] pub migrate_to: Option, pub seq: i64, @@ -72,7 +72,7 @@ pub struct RepoOp { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Tombstone { - pub did: String, + pub did: crate::types::string::Did, pub seq: i64, pub time: String, } diff --git a/atrium-api/src/com/atproto/temp/transfer_account.rs b/atrium-api/src/com/atproto/temp/transfer_account.rs index c24452f8..acc09261 100644 --- a/atrium-api/src/com/atproto/temp/transfer_account.rs +++ b/atrium-api/src/com/atproto/temp/transfer_account.rs @@ -3,16 +3,16 @@ #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Input { - pub did: String, - pub handle: String, + pub did: crate::types::string::Did, + pub handle: crate::types::string::Handle, pub plc_op: crate::records::Record, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Output { pub access_jwt: String, - pub did: String, - pub handle: String, + pub did: crate::types::string::Did, + pub handle: crate::types::string::Handle, pub refresh_jwt: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/atrium-api/src/types.rs b/atrium-api/src/types.rs index 726320f8..a65b28bc 100644 --- a/atrium-api/src/types.rs +++ b/atrium-api/src/types.rs @@ -14,6 +14,8 @@ pub use cid_link_json::CidLink; mod integer; pub use integer::*; +pub mod string; + /// Definitions for Blob types. /// Usually a map with `$type` is used, but deprecated legacy formats are also supported for parsing. /// diff --git a/atrium-api/src/types/string.rs b/atrium-api/src/types/string.rs new file mode 100644 index 00000000..b3c10938 --- /dev/null +++ b/atrium-api/src/types/string.rs @@ -0,0 +1,285 @@ +//! Lexicon [string formats]. +//! +//! [string formats]: https://atproto.com/specs/lexicon#string-formats + +use std::{cell::OnceCell, ops::Deref, str::FromStr}; + +use regex::Regex; +use serde::{de::Error, Deserialize, Deserializer, Serialize}; + +/// Common trait implementations for Lexicon string formats that are newtype wrappers +/// around `String`. +macro_rules! string_newtype { + ($name:ident) => { + impl FromStr for $name { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Self::new(s.into()) + } + } + + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Deserialize::deserialize(deserializer)?; + Self::new(value).map_err(D::Error::custom) + } + } + + impl Into for $name { + fn into(self) -> String { + self.0 + } + } + + impl AsRef for $name { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl Deref for $name { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_str() + } + } + }; +} + +/// An AT Protocol identifier. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(untagged)] +pub enum AtIdentifier { + Did(Did), + Handle(Handle), +} + +impl From for AtIdentifier { + fn from(did: Did) -> Self { + AtIdentifier::Did(did) + } +} + +impl From for AtIdentifier { + fn from(handle: Handle) -> Self { + AtIdentifier::Handle(handle) + } +} + +impl FromStr for AtIdentifier { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + if let Ok(did) = s.parse() { + Ok(AtIdentifier::Did(did)) + } else { + s.parse().map(AtIdentifier::Handle) + } + } +} + +impl Into for AtIdentifier { + fn into(self) -> String { + match self { + AtIdentifier::Did(did) => did.into(), + AtIdentifier::Handle(handle) => handle.into(), + } + } +} + +impl AsRef for AtIdentifier { + fn as_ref(&self) -> &str { + match self { + AtIdentifier::Did(did) => did.as_ref(), + AtIdentifier::Handle(handle) => handle.as_ref(), + } + } +} + +/// A generic [DID Identifier]. +/// +/// [DID Identifier]: https://atproto.com/specs/did +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(transparent)] +pub struct Did(String); +string_newtype!(Did); + +impl Did { + /// Parses a `Did` from the given string. + pub fn new(did: String) -> Result { + const RE_DID: OnceCell = OnceCell::new(); + + // https://atproto.com/specs/did#at-protocol-did-identifier-syntax + if !RE_DID + .get_or_init(|| Regex::new(r"^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$").unwrap()) + .is_match(&did) + { + Err("Invalid DID") + } else if did.len() > 2048 { + Err("DID too long") + } else { + Ok(Self(did)) + } + } + + /// Returns the DID method. + pub fn method(&self) -> &str { + &self.0[..4 + self.0[4..].find(':').unwrap()] + } + + /// Returns the DID as a string slice. + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + +/// A [Handle Identifier]. +/// +/// [Handle Identifier]: https://atproto.com/specs/handle +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(transparent)] +pub struct Handle(String); +string_newtype!(Handle); + +impl Handle { + /// Parses a `Handle` from the given string. + pub fn new(handle: String) -> Result { + const RE_HANDLE: OnceCell = OnceCell::new(); + + // https://atproto.com/specs/handle#handle-identifier-syntax + if !RE_HANDLE + .get_or_init(|| Regex::new(r"^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$").unwrap()) + .is_match(&handle) + { + Err("Invalid handle") + } else if handle.len() > 253 { + Err("Handle too long") + } else { + Ok(Self(handle)) + } + } + + /// Returns the handle as a string slice. + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + +#[cfg(test)] +mod tests { + use serde_json::from_str; + + use super::*; + + #[test] + fn valid_did() { + // From https://atproto.com/specs/did#examples + for valid in &[ + "did:plc:z72i7hdynmk6r22z27h6tvur", + "did:web:blueskyweb.xyz", + "did:method:val:two", + "did:m:v", + "did:method::::val", + "did:method:-:_:.", + "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N", + ] { + assert!( + from_str::(&format!("\"{}\"", valid)).is_ok(), + "valid DID `{}` parsed as invalid", + valid, + ); + } + } + + #[test] + fn invalid_did() { + // From https://atproto.com/specs/did#examples + for invalid in &[ + "did:METHOD:val", + "did:m123:val", + "DID:method:val", + "did:method:", + "did:method:val/two", + "did:method:val?two", + "did:method:val#two", + ] { + assert!( + from_str::(&format!("\"{}\"", invalid)).is_err(), + "invalid DID `{}` parsed as valid", + invalid, + ); + } + } + + #[test] + fn did_method() { + // From https://atproto.com/specs/did#examples + for (method, did) in &[ + ("did:plc", "did:plc:z72i7hdynmk6r22z27h6tvur"), + ("did:web", "did:web:blueskyweb.xyz"), + ("did:method", "did:method:val:two"), + ("did:m", "did:m:v"), + ("did:method", "did:method::::val"), + ("did:method", "did:method:-:_:."), + ( + "did:key", + "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N", + ), + ] { + assert_eq!(Did::new(did.to_string()).unwrap().method(), *method); + } + } + + #[test] + fn valid_handle() { + // From https://atproto.com/specs/handle#identifier-examples + for valid in &[ + "jay.bsky.social", + "8.cn", + "name.t--t", // not a real TLD, but syntax ok + "XX.LCS.MIT.EDU", + "a.co", + "xn--notarealidn.com", + "xn--fiqa61au8b7zsevnm8ak20mc4a87e.xn--fiqs8s", + "xn--ls8h.test", + "example.t", // not a real TLD, but syntax ok + // Valid syntax, but must always fail resolution due to other restrictions: + "2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion", + "laptop.local", + "blah.arpa", + ] { + assert!( + from_str::(&format!("\"{}\"", valid)).is_ok(), + "valid handle `{}` parsed as invalid", + valid, + ); + } + } + + #[test] + fn invalid_handle() { + // From https://atproto.com/specs/handle#identifier-examples + for invalid in &[ + "jo@hn.test", + "💩.test", + "john..test", + "xn--bcher-.tld", + "john.0", + "cn.8", + "www.masełkowski.pl.com", + "org", + "name.org.", + ] { + assert!( + from_str::(&format!("\"{}\"", invalid)).is_err(), + "invalid handle `{}` parsed as valid", + invalid, + ); + } + } +} diff --git a/atrium-cli/src/commands.rs b/atrium-cli/src/commands.rs index 0f0772fe..dcb7ef92 100644 --- a/atrium-cli/src/commands.rs +++ b/atrium-cli/src/commands.rs @@ -1,3 +1,4 @@ +use atrium_api::types::string::AtIdentifier; use clap::Parser; use std::str::FromStr; @@ -40,8 +41,8 @@ pub struct LoginArgs { #[derive(Parser, Debug)] pub struct ActorArgs { /// Actor's handle or did - #[arg(short, long)] - pub(crate) actor: Option, + #[arg(short, long, value_parser)] + pub(crate) actor: Option, } #[derive(Parser, Debug)] diff --git a/atrium-cli/src/runner.rs b/atrium-cli/src/runner.rs index c91c4871..33c17eb7 100644 --- a/atrium-cli/src/runner.rs +++ b/atrium-cli/src/runner.rs @@ -1,6 +1,7 @@ use crate::commands::Command; use crate::store::SimpleJsonFileSessionStore; use atrium_api::agent::{store::SessionStore, AtpAgent}; +use atrium_api::types::string::{AtIdentifier, Handle}; use atrium_api::xrpc::error::{Error, XrpcErrorKind}; use atrium_xrpc_client::reqwest::ReqwestClient; use chrono::Local; @@ -12,7 +13,7 @@ pub struct Runner { agent: AtpAgent, debug: bool, session_path: PathBuf, - handle: Option, + handle: Option, } impl Runner { @@ -74,7 +75,10 @@ impl Runner { .bsky .feed .get_author_feed(atrium_api::app::bsky::feed::get_author_feed::Parameters { - actor: args.actor.or(self.handle.clone()).unwrap(), + actor: args + .actor + .or(self.handle.clone().map(AtIdentifier::Handle)) + .unwrap(), cursor: None, filter: None, limit: Some(limit), @@ -125,7 +129,10 @@ impl Runner { .bsky .graph .get_follows(atrium_api::app::bsky::graph::get_follows::Parameters { - actor: args.actor.or(self.handle.clone()).unwrap(), + actor: args + .actor + .or(self.handle.clone().map(AtIdentifier::Handle)) + .unwrap(), cursor: None, limit: Some(limit), }) @@ -141,7 +148,10 @@ impl Runner { .bsky .graph .get_followers(atrium_api::app::bsky::graph::get_followers::Parameters { - actor: args.actor.or(self.handle.clone()).unwrap(), + actor: args + .actor + .or(self.handle.clone().map(AtIdentifier::Handle)) + .unwrap(), cursor: None, limit: Some(limit), }) @@ -157,7 +167,10 @@ impl Runner { .bsky .actor .get_profile(atrium_api::app::bsky::actor::get_profile::Parameters { - actor: args.actor.or(self.handle.clone()).unwrap(), + actor: args + .actor + .or(self.handle.clone().map(AtIdentifier::Handle)) + .unwrap(), }) .await, ); @@ -203,7 +216,7 @@ impl Runner { text: args.text, }, )), - repo: self.handle.clone().unwrap(), + repo: self.handle.clone().unwrap().into(), rkey: None, swap_commit: None, validate: None, @@ -221,7 +234,7 @@ impl Runner { .repo .delete_record(atrium_api::com::atproto::repo::delete_record::Input { collection: "app.bsky.feed.post".into(), - repo: self.handle.clone().unwrap(), + repo: self.handle.clone().unwrap().into(), rkey: args.uri.rkey, swap_commit: None, swap_record: None, diff --git a/lexicon/atrium-codegen/src/token_stream.rs b/lexicon/atrium-codegen/src/token_stream.rs index e1480dc4..988daafc 100644 --- a/lexicon/atrium-codegen/src/token_stream.rs +++ b/lexicon/atrium-codegen/src/token_stream.rs @@ -488,8 +488,15 @@ fn integer_type(integer: &LexInteger) -> Result<(TokenStream, TokenStream)> { fn string_type(string: &LexString) -> Result<(TokenStream, TokenStream)> { let description = description(&string.description); - // TODO: format, enum? - Ok((description, quote!(String))) + // TODO: enum? + let typ = match string.format { + Some(LexStringFormat::AtIdentifier) => quote!(crate::types::string::AtIdentifier), + Some(LexStringFormat::Did) => quote!(crate::types::string::Did), + Some(LexStringFormat::Handle) => quote!(crate::types::string::Handle), + // TODO: other formats + _ => quote!(String), + }; + Ok((description, typ)) } fn unknown_type(unknown: &LexUnknown, name: Option<&str>) -> Result<(TokenStream, TokenStream)> {